Skip to content

Feature: Configuration of the battery capacity#2465

Open
SW-Niko wants to merge 4 commits intohoylabs:developmentfrom
SW-Niko:BattCapacity
Open

Feature: Configuration of the battery capacity#2465
SW-Niko wants to merge 4 commits intohoylabs:developmentfrom
SW-Niko:BattCapacity

Conversation

@SW-Niko
Copy link
Copy Markdown

@SW-Niko SW-Niko commented Apr 11, 2026

Enables configuration of:

  • Battery nominal capacity [Ah]
  • Battery nominal voltage [V]

This information is required for the Solar-Surplus calculation.
You can find more information here: Link

grafik

SW-Niko added 2 commits April 11, 2026 08:49
add the nominal voltage and the capacity to config and webUI
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2667de60-61ea-43a4-9f05-6f33a9eed133

📥 Commits

Reviewing files that changed from the base of the PR and between 75c3bea and 0b2af4f.

📒 Files selected for processing (10)
  • include/battery/Stats.h
  • include/battery/pytes/Stats.h
  • src/battery/HassIntegration.cpp
  • src/battery/Stats.cpp
  • src/battery/pytes/HassIntegration.cpp
  • src/battery/pytes/Provider.cpp
  • src/battery/pytes/Stats.cpp
  • webapp/src/locales/de.json
  • webapp/src/locales/en.json
  • webapp/src/views/BatteryAdminView.vue
💤 Files with no reviewable changes (3)
  • include/battery/pytes/Stats.h
  • src/battery/pytes/HassIntegration.cpp
  • src/battery/pytes/Stats.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • webapp/src/views/BatteryAdminView.vue
  • webapp/src/locales/en.json
  • webapp/src/locales/de.json
👮 Files not reviewed due to content moderation or server errors (4)
  • include/battery/Stats.h
  • src/battery/HassIntegration.cpp
  • src/battery/pytes/Provider.cpp
  • src/battery/Stats.cpp

Walkthrough

Adds nominal battery metadata fields to configuration and runtime stats, propagates them through CAN provider parsing, live-view and MQTT publishing (Home Assistant), and exposes editable fields and localization in the web frontend.

Changes

Cohort / File(s) Summary
Backend Configuration
include/Configuration.h, src/Configuration.cpp
Added NominalVoltage (float) and NominalCapacity (uint16_t) to BATTERY_CONFIG_T and updated JSON serialize/deserialize to use nominal_voltage / nominal_capacity.
Battery Stats (core)
include/battery/Stats.h, src/battery/Stats.cpp
Added optional getters getNominalCapacity() / getNominalVoltage() and internal setters/state. Live-view and MQTT publishing now include nominalCapacity and nominalVoltage when available.
Pytes battery provider & stats
include/battery/pytes/Stats.h, src/battery/pytes/Stats.cpp, src/battery/pytes/Provider.cpp
Removed private _totalCapacity; provider now writes nominal capacity via _stats->setNominalCapacity(...) and uses a local capacity variable for SOC calculations. Pytes Stats no longer emits/publishes total capacity.
Home Assistant MQTT integration
src/battery/HassIntegration.cpp, src/battery/pytes/HassIntegration.cpp
Added HA sensor registrations for nominalCapacity and nominalVoltage in main integration; removed the “Total Capacity” sensor registration from the Pytes integration.
Frontend types & UI
webapp/src/types/BatteryConfig.ts, webapp/src/views/BatteryAdminView.vue
Extended BatteryConfig type with nominal_voltage and nominal_capacity. Added conditional numeric inputs in BatteryAdmin UI bound to these fields (voltage: 0–100 step 0.1 V; capacity: 0–500 step 1 Ah; capacity input hidden when provider == 4).
Localization
webapp/src/locales/en.json, webapp/src/locales/de.json
Added BatteryDataConfiguration, NominalVoltage, NominalVoltageInfo, NominalCapacity, NominalCapacityInfo keys; replaced battery.capacity with battery.nominalCapacity and battery.nominalVoltage.

Sequence Diagram(s)

sequenceDiagram
    participant CAN as CAN Bus
    participant Provider as Pytes Provider
    participant Stats as Battery Stats
    participant MQTT as MQTT / HA
    participant Live as Web LiveView

    CAN->>Provider: delivers message (e.g., 0x379 / 0x409)
    Provider->>Stats: setNominalCapacity(capacity)
    Provider->>Stats: update SOC using local capacity
    Stats->>MQTT: publish nominalCapacity / nominalVoltage (if present)
    Stats->>Live: include nominalCapacity / nominalVoltage in live-view JSON (if present)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 Two small fields hopped into the stack,
Voltage and capacity found their track,
From CAN to UI they gently roam,
In JSON, MQTT, and a cozy home,
I nibble code and give them a snack.

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly describes the feature being implemented: enabling configuration of battery nominal capacity and nominal voltage for Solar-Surplus calculation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Configuration.cpp (1)

142-143: Serialize from the function argument instead of global state.

Line [142]-Line [143] ignore source and read from config.Battery, which makes this serializer context-dependent and harder to reuse/test.

♻️ Proposed fix
-    target["nominal_voltage"] = config.Battery.NominalVoltage;
-    target["nominal_capacity"] = config.Battery.NominalCapacity;
+    target["nominal_voltage"] = source.NominalVoltage;
+    target["nominal_capacity"] = source.NominalCapacity;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Configuration.cpp` around lines 142 - 143, The serializer is reading from
the global config.Battery instead of the function argument 'source', making it
context-dependent; update the assignments so target["nominal_voltage"] and
target["nominal_capacity"] use source.Battery.NominalVoltage and
source.Battery.NominalCapacity (replace references to config.Battery with
source.Battery) so the serializer operates on the provided source object rather
than global state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Configuration.cpp`:
- Around line 577-578: Validate and sanitize the incoming nominal battery fields
instead of trusting the client: read source["nominal_voltage"] and
source["nominal_capacity"], ensure they are numeric and within allowed ranges
(e.g. nominal_voltage > 0 and within a sane upper limit, and nominal_capacity >
0 and within a sensible max), and then assign to target.NominalVoltage and
target.NominalCapacity only after validation; on out-of-range or non-numeric
values either clamp to the permitted min/max or return/raise a validation error
and log the rejection (include the field names nominal_voltage/nominal_capacity
and the offending value in the log) so invalid payloads cannot propagate into
Solar‑Surplus calculations.

---

Nitpick comments:
In `@src/Configuration.cpp`:
- Around line 142-143: The serializer is reading from the global config.Battery
instead of the function argument 'source', making it context-dependent; update
the assignments so target["nominal_voltage"] and target["nominal_capacity"] use
source.Battery.NominalVoltage and source.Battery.NominalCapacity (replace
references to config.Battery with source.Battery) so the serializer operates on
the provided source object rather than global state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74995799-3ff0-4d3c-a9fe-aa06dcd7ce0d

📥 Commits

Reviewing files that changed from the base of the PR and between d3bc11c and 7496602.

📒 Files selected for processing (6)
  • include/Configuration.h
  • src/Configuration.cpp
  • webapp/src/locales/de.json
  • webapp/src/locales/en.json
  • webapp/src/types/BatteryConfig.ts
  • webapp/src/views/BatteryAdminView.vue

Comment thread src/Configuration.cpp
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 11, 2026

Build Artifacts

Firmware built from this pull request's code:

Notice

  • These artifacts are ZIP files containing the factory update binary as well as the OTA update binary.
    Extract the binaries from the ZIP files first. Do not use the ZIP files themselves to perform an update.
  • These links point to artifacts of the latest successful build run.
  • The linked artifacts were built from bef44c6.

read from source instead from  config.Battery
@AndreasBoehm
Copy link
Copy Markdown
Member

What if the used Battery Provider already provides the capacity? Can we make use of it and hide the input field?

e.g.
Screenshot 2026-04-11 at 10 48 31

Another topic is that merging this without merging the surplus feature as well does not make much sense. I know that this is a problem that i created because i don't have enough capacity to look into bigger PRs : /

I am very happy about all the effort you put into this project!!

Do you have any suggestions on how to improve the workflow? Having small/tiny PRs is amazing and i want to keep going like that.
We could think about feature branches in the hoylabs repo (e.g. surplus) where we can merge MRs related to that feature and when the whole feature is ready we merge the feature branch into development. Thats just a first idea.

Or maybe feature-flags using #define ?

@SW-Niko
Copy link
Copy Markdown
Author

SW-Niko commented Apr 11, 2026

What if the used Battery Provider already provides the capacity? Can we make use of it and hide the input field?

Yes sure. The less configuration required, the fewer mistakes will be made.

That's a good suggestion. 👍

  • If the battery provider already provides this information we hide the input field.
  • If the information is available, either by configuration or by the battery provider we display the value in the live view.

I will update the PR, but will need some time to check all available provider.

We could think about feature branches

To be honest ... I don't have much experience leading software projects.
In the meantime I found a good way for myself. Providing my features on my fork.
This enables early testing for everyone and I'm still open to integrate everything here.

@spcqike
Copy link
Copy Markdown

spcqike commented Apr 11, 2026

@AndreasBoehm i Like Both ideas. Branches and #defines.

The following will be OT in this PR:

I too startet working on local forks. I would like to contribute, but I don’t know how, as some small steps wouldn’t make sense without the overall goal. But all together might take some time and could be big.

Like, a small step would be my „https powermeter voltage“ branch (https://github.com/spcqike/OpenDTU-OnBattery/tree/https_powermeter_voltage ), which lets you also query the voltages. Like some meters already do (Victron, SML)
My overall goal is to make the DPL aware of the powermeter voltage, to limit inverters to not exceed 252V on the inverter side. (https://github.com/spcqike/OpenDTU-OnBattery/tree/variant_one)

tjis is already working, but not perfect.

I also created a #define firmware. (https://github.com/spcqike/OpenDTU-OnBattery/tree/reduce-OTA-size) as mentioned in #2459
this works, but is heavily AI coded. I think it makes sense, but I wouldn’t use it in this state to merge it, as there are some bugs, which I found later on. But one can define nearly every single part in or out (I needed an up to date but 2Mb firmware for remote OTA flashing)

So, I also would like to contribute smaller steps, even if they don’t make sense on themself, and I would like to use defines, also later on (like tasmota does) as I think it’s a great feature to customize / reduce firmware later on.

- add nominal capacity and nominal voltage to stats
- in general use information from the provider if available, up to now only pytes
- remove redundant values from pytes
- hide configuration values on the WebUI if they are not necessary
@SW-Niko
Copy link
Copy Markdown
Author

SW-Niko commented Apr 13, 2026

I've checked all battery providers.
Currently, only the Pytes provider is supplying the capacity.
I've implemented the following changes:

  • If the provider supplies the nominal capacity and/or nominal voltage, this information is used, and the configuration is hidden in the web UI.
  • If the provider doesn't supply this data, the configuration data is used.
  • If data is available, it will be displayed in the battery live view.
  • The general Stats handles MQTT and HASS for both values.

All text have been changed to "Nominal charge" and "Nominal voltage."
The duplicate displays in Pytes have been removed.

Note: This changed the MQTT topic from "Total Charge" to "Nominal charge."
We can change it back to "Total Charge," but then I would also change all other displays uniformly to "Total Charge".
The term "Nominal charge" just seemed better and more descriptive to me.

I haven't gone through the battery data sheets to see if we can read the data.
I might do that sometime for the smart shunt.

@SW-Niko
Copy link
Copy Markdown
Author

SW-Niko commented Apr 14, 2026

Hello @vaterlangen,

Could you please take a look at this PR? I suspect the Zendure Battery is also affected.
Do I understand correctly that it can only be used with inverters powered by Smart Batteries?

If so, then the provider would need to be adjusted to the changed general stats, and the duplicate data in the Zenture Provider would need to be removed.

Would you prefer to adjust this yourself later, or should I change it now?

@vaterlangen
Copy link
Copy Markdown

@SW-Niko I have also changes pending in #2451 where I (due to different dataset available via local and official API) shifted to std::optional for the values within stats. Wouldn't that also help here, in case no battery capacity has been reported?

Currently I have other stuff pending, so no much time for looking at this....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants