Skip to content

Battery usermod/general update 2026#5399

Open
itCarl wants to merge 10 commits intowled:mainfrom
itCarl:battery-usermod/general-updates-2026
Open

Battery usermod/general update 2026#5399
itCarl wants to merge 10 commits intowled:mainfrom
itCarl:battery-usermod/general-updates-2026

Conversation

@itCarl
Copy link
Contributor

@itCarl itCarl commented Feb 25, 2026

Major update to the Battery usermod with new features, improved accuracy, and better code organization.

New features

  • new LiFePO4 battery type
  • Estimated runtime — auto-detected when INA226 usermod is present, no configuration needed
  • Coulomb counting — integrates current over time for more accurate SoC tracking, recalibrates from voltage at rest (OCV)
  • Current smoothing (EMA) for stable runtime estimates despite load fluctuations

INA226 integration

The battery usermod now discovers INA226. Just list both in custom_usermods and it works — no build flags needed. If INA226 isn't present, the features stay hidden and everything else works as before.

Charging detection

added charging detection logic.

Code cleanup

  • Removed dead code (empty overrides, commented-out methods, boilerplate template comments)
  • Merged duplicate config reading logic into readFromConfig()
  • Reorganized methods into logical sections

Documentation

  • Added "Estimated Runtime" section to readme covering setup, how it works, and accuracy expectations
  • Added step-by-step guide for adding custom battery types
  • Referenced MAX17048 usermod as a hardware fuel gauge alternative

Summary by CodeRabbit

  • New Features

    • LiFePO4 battery support
    • Charging-state detection via voltage trends
    • Estimated runtime reporting (uses INA226 if present)
    • Coulomb counting for improved state tracking
    • Dynamic MQTT/Home Assistant autodiscovery for battery sensors
    • Ability to add custom battery types
  • Changes

    • Default battery type set to LiPo
    • Battery voltage, level, charging and runtime exposed via JSON and UM data
  • Documentation

    • Readme updated with runtime, charging, and custom-type guides

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Factory-based battery instantiation added with LiPo, Li‑Ion and LiFePO4 support; voltage-to‑SoC mapping moved to LUT interpolation. Charging detection (voltage-trend) and estimated runtime (coulomb counting with optional INA226) implemented. MQTT/Home Assistant autodiscovery and UM data inter-mod exposure added.

Changes

Cohort / File(s) Summary
Battery core & runtime
usermods/Battery/Battery.cpp
Reworked setup()/loop() to use factory-created UMBattery; added sliding-window voltage-trend charging detection, INA226 probing, coulomb counting/rest recalibration, runtime estimation, UM data exposure, MQTT sensor registration/autodiscovery, and JSON/state helpers.
Battery base API
usermods/Battery/UMBattery.h
Added LutEntry and lutInterpolate(), changed mapVoltage to float mapVoltage(float), added calculateAndSetLevel(), changed level type to int8_t, and added virtual destructor.
Defaults & enums
usermods/Battery/battery_defaults.h
Default type set to LiPo; removed unknown; added LiFePO4 min/max macros, USERMOD_BATTERY_CAPACITY, and updated batteryType enum.
Chemistry implementations (LUTs)
usermods/Battery/types/LipoUMBattery.h, usermods/Battery/types/LionUMBattery.h, usermods/Battery/types/Lifepo4UMBattery.h
Switched from parametric mapping to PROGMEM LUT-based discharge curves; unified mapVoltage(float) signature; added new Lifepo4UMBattery and LUT data.
Removed type
usermods/Battery/types/UnkownUMBattery.h
Deleted Unknown battery implementation and its mapping/update code.
Docs
usermods/Battery/readme.md
Documented charging detection, estimated runtime (INA226), LiFePO4 support, custom battery type guide, and changelog updates.
INA226 inter-mod exposure
usermods/INA226_v2/INA226_v2.cpp
Allocates um_data_t slots and exposes INA226 current/voltage/power via new getUMData(um_data_t **data) when enabled and valid readings exist.
Misc config
.claude/settings.local.json
Added local JSON with allowed CLI command strings (non-functional config entry).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • More usermod fixes #4623 — Modifies INA226_v2 to expose INA226 readings via getUMData enabling inter-usermod data sharing.

Suggested reviewers

  • willmmiles
  • softhack007
  • DedeHai
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Battery usermod/general update 2026' is vague and generic, using non-descriptive terms that don't convey the specific nature of the significant changes made to the codebase. Revise the title to highlight a primary change, such as 'Add LiFePO4 battery type and estimated runtime feature' or 'Refactor Battery usermod with factory pattern and INA226 integration'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
Contributor

@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: 8

🧹 Nitpick comments (1)
usermods/Battery/UMBattery.h (1)

45-53: Add a zero-span guard in LUT interpolation.

If two adjacent LUT entries have equal voltage, Line 51 divides by zero and produces invalid percentages. This is easy to hit in custom battery definitions.

🛡️ Defensive interpolation guard
-                if (v >= lo.voltage) {
-                    float ratio = (v - lo.voltage) / (hi.voltage - lo.voltage);
+                if (v >= lo.voltage) {
+                    const float span = hi.voltage - lo.voltage;
+                    if (fabsf(span) < 1e-6f) return hi.percent;
+                    float ratio = (v - lo.voltage) / span;
                     return lo.percent + ratio * (hi.percent - lo.percent);
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/UMBattery.h` around lines 45 - 53, The LUT interpolation
loop in UMBattery.h can divide by zero when adjacent LutEntry voltages are
equal; inside the loop that iterates over lut (using variables hi, lo, v, size
and type LutEntry) add a zero-span guard: detect when hi.voltage == lo.voltage
and handle it (e.g., return lo.percent or the average of lo.percent and
hi.percent) instead of computing ratio = (v - lo.voltage) / (hi.voltage -
lo.voltage); ensure the guard is placed before calculating ratio so the method
that performs interpolation returns a valid percentage for equal-voltage
entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 299-345: When getINA226Current() returns a negative value
(current_A < 0) the code path exits without clearing runtime telemetry, leaving
estimatedTimeLeft and smoothedCurrent stale; update the block that checks
estimatedRuntimeEnabled/getINA226Current (referencing getINA226Current,
estimatedRuntimeEnabled, current_A, estimatedTimeLeft, smoothedCurrent) so that
whenever current_A is invalid (current_A < 0) or estimatedRuntimeEnabled is
false you explicitly reset estimatedTimeLeft = -1 and smoothedCurrent = -1.0f
(and avoid doing Coulomb updates) so no stale runtime value is published.
- Around line 397-398: The JSON "Next update" countdown uses (nextReadTime -
millis())/1000 which can be negative when the read time has passed; change the
code that builds infoNextUpdate (the calls around
infoNextUpdate.add((nextReadTime - millis()) / 1000); infoNextUpdate.add(F("
sec")); ) to compute a non-negative remainingSeconds first (e.g.
remainingSeconds = (nextReadTime > millis()) ? (nextReadTime - millis())/1000 :
0) and then add remainingSeconds and " sec" to infoNextUpdate so the displayed
countdown is clamped to 0.
- Around line 104-121: The low-power logic calls bat->getLevel() before a valid
battery reading exists; modify lowPowerIndicator() to first verify the battery
has a valid/initialized level (e.g., with an existing isValid/hasLevel method or
by checking getLevel() against a clear sentinel like <0 or NaN) and early-return
if the level is invalid, then use the validated value in the existing checks
(lowPowerIndicationDone, lowPowerIndicatorReactivationThreshold,
lowPowerIndicatorThreshold) so
applyPreset(lastPreset)/applyPreset(lowPowerIndicatorPreset) cannot be triggered
until a real battery level is available; update references in
lowPowerIndicator(), lowPowerIndicationDone, lowPowerIndicatorActive,
lowPowerActivationTime, lastPreset, currentPreset and lowPowerIndicatorDuration
accordingly.
- Around line 586-594: The runtime sensor registration is currently skipped in
onMqttConnect() because it checks estimatedRuntimeEnabled which is only set
after the INA226 probe runs in loop(), causing discovery to be missed; remove
the estimatedRuntimeEnabled gate from onMqttConnect() (always call
registerMqttSensor("runtime", F("Runtime"), "sensor", "duration", "min") when
HomeAssistantDiscovery is true) and instead perform the actual enablement/state
publishing when the probe completes — i.e., after the INA226 probe success code
where estimatedRuntimeEnabled is set (or in the probe callback), publish the
runtime value or availability. Ensure references: onMqttConnect,
registerMqttSensor, estimatedRuntimeEnabled, and the INA226 probe completion
path are updated accordingly.
- Around line 132-136: In getINA226Current(), before dereferencing
data->u_data[0], add defensive checks that data->u_size >= 1, data->u_data is
non-null, and data->u_data[0] is non-null (and optionally ensure the pointed
value is a float via proper cast) and return -1.0f on failure; update the
function (which currently calls UsermodManager::getUMData(&data,
USERMOD_ID_INA226)) to perform these validations and only then cast and return
*(float*)data->u_data[0].

In `@usermods/Battery/readme.md`:
- Around line 298-301: Update the changelog date string "2025-02-25" to the
correct PR creation date "2026-02-25" in the Battery readme (search for the
exact token "2025-02-25" in usermods/Battery/readme.md) so the entry aligns with
the PR timestamp.

In `@usermods/Battery/types/UnknownUMBattery.h`:
- Around line 24-25: Add the missing guarded macro definitions for
USERMOD_BATTERY_UNKNOWN_MIN_VOLTAGE and USERMOD_BATTERY_UNKNOWN_MAX_VOLTAGE into
battery_defaults.h so UnknownUMBattery.h can compile; specifically, define
USERMOD_BATTERY_UNKNOWN_MIN_VOLTAGE as 2.6f and
USERMOD_BATTERY_UNKNOWN_MAX_VOLTAGE as 4.2f using `#ifndef/`#define/#endif guards
to match the style of the other battery type macros referenced by
UnknownUMBattery.h.

In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 347-351: getUMData currently returns true based on _lastLoopCheck
which is set when sampling starts, allowing stale defaults to be exposed; add a
definitive "measurement complete" guard (e.g. a boolean _measurementReady or a
timestamp _lastMeasurementComplete) that is set only after raw sensor values are
actually read and parsed in the sampling code, and cleared when a new
measurement is triggered, then update getUMData to check that _measurementReady
(or that _lastMeasurementComplete is newer than the measurement start time) in
addition to _settingEnabled before returning um_data; ensure the sampling path
sets/clears that flag/timestamp in the functions that start sampling and the
function that consumes sensor data so getUMData only returns true after a
finished measurement.

---

Nitpick comments:
In `@usermods/Battery/UMBattery.h`:
- Around line 45-53: The LUT interpolation loop in UMBattery.h can divide by
zero when adjacent LutEntry voltages are equal; inside the loop that iterates
over lut (using variables hi, lo, v, size and type LutEntry) add a zero-span
guard: detect when hi.voltage == lo.voltage and handle it (e.g., return
lo.percent or the average of lo.percent and hi.percent) instead of computing
ratio = (v - lo.voltage) / (hi.voltage - lo.voltage); ensure the guard is placed
before calculating ratio so the method that performs interpolation returns a
valid percentage for equal-voltage entries.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4178d05 and 9149d8b.

📒 Files selected for processing (10)
  • usermods/Battery/Battery.cpp
  • usermods/Battery/UMBattery.h
  • usermods/Battery/battery_defaults.h
  • usermods/Battery/readme.md
  • usermods/Battery/types/Lifepo4UMBattery.h
  • usermods/Battery/types/LionUMBattery.h
  • usermods/Battery/types/LipoUMBattery.h
  • usermods/Battery/types/UnknownUMBattery.h
  • usermods/Battery/types/UnkownUMBattery.h
  • usermods/INA226_v2/INA226_v2.cpp
💤 Files with no reviewable changes (1)
  • usermods/Battery/types/UnkownUMBattery.h

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
usermods/Battery/Battery.cpp (2)

260-266: ⚠️ Potential issue | 🟠 Major

Runtime HA discovery can still be missed until MQTT reconnect.

If INA226 becomes available after onMqttConnect(), Line 608’s gate means runtime entity discovery won’t happen in that session. Register runtime when probe succeeds in loop as well.

📡 Suggested fix
       if (!ina226Probed) {
         um_data_t *data = nullptr;
         if (UsermodManager::getUMData(&data, USERMOD_ID_INA226) && data) {
           estimatedRuntimeEnabled = true;
           ina226Probed = true;
+#ifndef WLED_DISABLE_MQTT
+          if (HomeAssistantDiscovery && WLED_MQTT_CONNECTED) {
+            registerMqttSensor("runtime", F("Runtime"), "sensor", "duration", "min");
+          }
+#endif
         }
       }

Also applies to: 593-610

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 260 - 266, When the INA226 probe
succeeds in the loop (where ina226Probed and estimatedRuntimeEnabled are set),
also invoke the same runtime/entity registration that onMqttConnect() performs
so discovery is registered immediately instead of waiting for an MQTT reconnect;
update the probe-success block (where ina226Probed and estimatedRuntimeEnabled
are set) to call the runtime registration function used by onMqttConnect() (the
same routine that publishes/registers runtime entities) so runtime discovery
occurs right away.

134-138: ⚠️ Potential issue | 🟡 Minor

Harden getINA226Current() before dereference.

Line 137 dereferences data->u_data[0] without validating slot availability. Add minimal shape checks to avoid UB if contract ever shifts.

🛡️ Suggested fix
 float getINA226Current() {
   um_data_t *data = nullptr;
   if (!UsermodManager::getUMData(&data, USERMOD_ID_INA226) || !data) return -1.0f;
+  if (data->u_size < 1 || !data->u_data || !data->u_data[0]) return -1.0f;
   return *(float*)data->u_data[0];
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 134 - 138, getINA226Current
currently dereferences data->u_data[0] without validating the u_data array or
its first slot; update getINA226Current to first ensure
UsermodManager::getUMData returned true, then verify data is non-null,
data->u_data is non-null, and data->u_data[0] is non-null (and if um_data_t
exposes a count/length field check it is > 0) before casting to float* and
returning the value, otherwise return -1.0f to preserve the existing error path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 269-277: The first-voltage and config-save paths call
readVoltage() even when batteryPin == -1; before any call to readVoltage()
(e.g., in the isFirstVoltageReading block where bat->setVoltage(readVoltage())
and in the config-save path), add a guard that checks batteryPin >= 0 and skip
the read/set when batteryPin is invalid (return early or bypass the ADC read),
so no ADC access occurs when batteryPin is -1.

In `@usermods/Battery/readme.md`:
- Around line 89-90: Replace the typo "Lithium Ionen" with "Lithium Ion" in the
battery chemistry table entry so it matches the rest of the documentation and
UI; update the row that maps to the `USERMOD_BATTERY_LION_MIN_VOLTAGE` symbol
(the table line for lion (Li-Ion)) accordingly.

---

Duplicate comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 260-266: When the INA226 probe succeeds in the loop (where
ina226Probed and estimatedRuntimeEnabled are set), also invoke the same
runtime/entity registration that onMqttConnect() performs so discovery is
registered immediately instead of waiting for an MQTT reconnect; update the
probe-success block (where ina226Probed and estimatedRuntimeEnabled are set) to
call the runtime registration function used by onMqttConnect() (the same routine
that publishes/registers runtime entities) so runtime discovery occurs right
away.
- Around line 134-138: getINA226Current currently dereferences data->u_data[0]
without validating the u_data array or its first slot; update getINA226Current
to first ensure UsermodManager::getUMData returned true, then verify data is
non-null, data->u_data is non-null, and data->u_data[0] is non-null (and if
um_data_t exposes a count/length field check it is > 0) before casting to float*
and returning the value, otherwise return -1.0f to preserve the existing error
path.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9149d8b and b6e8fd9.

📒 Files selected for processing (4)
  • usermods/Battery/Battery.cpp
  • usermods/Battery/UMBattery.h
  • usermods/Battery/readme.md
  • usermods/INA226_v2/INA226_v2.cpp

Copy link
Contributor

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
usermods/Battery/Battery.cpp (1)

142-179: ⚠️ Potential issue | 🟡 Minor

StaticJsonDocument<600> may be too small for HA discovery payloads, silently truncating fields.

The discovery document includes a nested device sub-object (5 fields with string values) on top of the sensor-level fields. ArduinoJson v6 stores string copies in the static buffer, so the effective capacity can be consumed faster than the serialised byte count suggests. When the document is full, subsequent operator[] assignments are silently dropped.

json_str[1024] already allocates enough stack for a larger document — increase the document capacity to match:

♻️ Proposed fix
-      StaticJsonDocument<600> doc;
+      StaticJsonDocument<1024> doc;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 142 - 179, The
StaticJsonDocument<600> in addMqttSensor is too small and can silently drop
assignments when building the Home Assistant discovery payload (strings are
stored in the JSON buffer); increase the StaticJsonDocument capacity to match
the available json_str buffer (e.g., ~1024 or slightly larger) so the nested
device object and sensor fields fit, then recompile and verify serializeJson
returns the full payload size before calling mqtt->publish; reference the
addMqttSensor function, the StaticJsonDocument<600> declaration, and
json_str[1024] when making the change.
🧹 Nitpick comments (1)
usermods/Battery/Battery.cpp (1)

20-20: Unnecessary default-constructed LipoUMBattery allocation at field initialization.

bat is unconditionally deleted at the start of setup() and replaced by the factory, so the inline new LipoUMBattery() allocation is wasted on every boot.

♻️ Proposed refactor
-    UMBattery* bat = new LipoUMBattery();
+    UMBattery* bat = nullptr;

Then guard bat->update(cfg) and any other pre-setup() accesses accordingly (they already are, since setup() is always called first).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` at line 20, The field-level allocation
"UMBattery* bat = new LipoUMBattery();" wastes a default-constructed
LipoUMBattery because setup() immediately deletes and replaces it; remove the
inline new and initialize bat to nullptr (or equivalent) instead, update any
pre-setup usages by guarding calls like bat->update(cfg) behind a nullptr check,
and ensure setup() still constructs the correct battery via the factory and
handles deletion appropriately in the setup() method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 48-49: Replace the magic literal in the array declaration with the
constant so the array size stays in sync: change the declaration of
voltageHistory to use VOLTAGE_HISTORY_SIZE (referring to the existing static
const uint8_t VOLTAGE_HISTORY_SIZE) instead of the hardcoded 5, ensuring
voltageHistory is sized using the symbolic constant.
- Around line 362-364: Auto-off currently triggers on startup because
bat->getLevel() returns -1 until the first ADC read; modify the auto-off check
in the Battery class so it only runs when a valid battery level exists (same
guard used for lowPowerIndicator()). Concretely, in the block referencing
autoOffEnabled, autoOffThreshold, bat->getLevel() and turnOff(), add a condition
that bat->getLevel() >= 0 (or reuse the existing validity check used by
lowPowerIndicator()) before comparing to autoOffThreshold so turnOff() is not
called on the initial invalid reading.
- Around line 334-338: When performing the rest recalibration in the block that
checks current_A against REST_CURRENT_THRESHOLD, guard against the
bat->getLevel() sentinel value (−1) before assigning coulombSoC: read int level
= bat->getLevel() and only set coulombSoC = level / 100.0f when level >= 0;
otherwise skip the recalibration so the existing constrained SoC logic
(constrain(...) / Coulomb counter) remains intact. Ensure you still update
atRest and restStartTime as currently done, but avoid using the −1 result to
overwrite coulombSoC.
- Around line 238-239: The umLevel field is declared as int8_t but is being
advertised as UMT_BYTE which treats it as unsigned, causing the -1 sentinel to
appear as 255; fix by either changing the advertised type to a signed container
(set um_data->u_type[1] = UMT_INT16 and ensure um_data->u_data[1] points to a
matching int16_t storage) or change the umLevel storage to uint8_t and use an
unsigned sentinel (e.g., 0xFF) and keep u_type[1] = UMT_BYTE; update any related
sentinel checks to match the chosen representation (refer to umLevel,
um_data->u_data[1], and um_data->u_type[1]).

In `@usermods/Battery/readme.md`:
- Line 194: Replace the ambiguous phrase "no extra configuration needed" in the
Battery readme with a clarification that the INA226 usermod must still be added
to custom_usermods (as shown in the Setup section) but that no additional build
flags or compile-time configuration are required; reference the INA226 usermod
name (INA226_v2) and the custom_usermods entry so readers know where to look.
- Around line 138-145: The header currently defines the static data
MyBattery::dischargeLut and MyBattery::dischargeLutSize which causes an
ODR/multiple-definition linker error if MyBattery.h is included from multiple
translation units; fix it by making those definitions inline (C++17) or moving
their non-inline definitions into a single .cpp (e.g., Battery.cpp) so only one
translation unit emits them—update the declarations/definitions for
MyBattery::dischargeLut and MyBattery::dischargeLutSize accordingly (or add a
comment instructing users to include the header only from Battery.cpp if you
choose the .cpp-only approach).

---

Outside diff comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 142-179: The StaticJsonDocument<600> in addMqttSensor is too small
and can silently drop assignments when building the Home Assistant discovery
payload (strings are stored in the JSON buffer); increase the StaticJsonDocument
capacity to match the available json_str buffer (e.g., ~1024 or slightly larger)
so the nested device object and sensor fields fit, then recompile and verify
serializeJson returns the full payload size before calling mqtt->publish;
reference the addMqttSensor function, the StaticJsonDocument<600> declaration,
and json_str[1024] when making the change.

---

Nitpick comments:
In `@usermods/Battery/Battery.cpp`:
- Line 20: The field-level allocation "UMBattery* bat = new LipoUMBattery();"
wastes a default-constructed LipoUMBattery because setup() immediately deletes
and replaces it; remove the inline new and initialize bat to nullptr (or
equivalent) instead, update any pre-setup usages by guarding calls like
bat->update(cfg) behind a nullptr check, and ensure setup() still constructs the
correct battery via the factory and handles deletion appropriately in the
setup() method.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6e8fd9 and d1ec2c4.

📒 Files selected for processing (2)
  • usermods/Battery/Battery.cpp
  • usermods/Battery/readme.md

@netmindz netmindz added the usermod usermod related label Feb 27, 2026
Copy link
Contributor

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 325-327: The code currently integrates Coulombs using dt_hours =
(now - lastCoulombTime)/3600000.0f (variable lastCoulombTime) even when previous
current samples were invalid, causing a huge erroneous integration after a gap;
modify the path that handles invalid current samples (the block around the logic
at lines 358-362) to update/reset lastCoulombTime to the current timestamp (now)
whenever you drop or mark current data invalid so that the next valid sample
uses a zero dt interval; ensure the same reset is applied wherever current is
ignored to prevent integrating over stale gaps.
- Around line 382-387: Update the block comment describing Battery data exported
by getUMData() so slot 1's documented type matches the actual exported type:
change the description of "slot 1" from "int8_t, 0-100%" to "int16_t, 0-100%"
(and optionally note UMT_INT16), ensuring the comment in Battery.cpp near the
getUMData() / UM-data slots list accurately reflects that slot 1 uses int16_t
rather than int8_t.
- Line 39: The code computes lowPowerIndicatorReactivationThreshold by adding 10
to lowPowerIndicatorThreshold without clamping, which can exceed 100 or wrap for
uint8_t; update all places that set lowPowerIndicatorReactivationThreshold (the
occurrences that use lowPowerIndicatorThreshold + 10) to first clamp
lowPowerIndicatorThreshold into a safe range (0..100) and compute the
reactivation threshold using a wider integer then cap to 100, e.g. temp =
std::min(100, std::max(0, int(lowPowerIndicatorThreshold) + 10));
lowPowerIndicatorReactivationThreshold = static_cast<uint8_t>(temp); apply the
same clamp logic wherever lowPowerIndicatorReactivationThreshold is computed.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1ec2c4 and 51160d4.

📒 Files selected for processing (3)
  • .claude/settings.local.json
  • usermods/Battery/Battery.cpp
  • usermods/Battery/readme.md
✅ Files skipped from review due to trivial changes (1)
  • .claude/settings.local.json

uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clamp low-power thresholds to keep reactivation reachable.

Line 39 and Line 578 compute lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10 without capping. With thresholds >90, reactivation can become impossible (>100), and high config values can also overflow behavior.

🔧 Suggested fix
-    uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
+    uint8_t lowPowerIndicatorReactivationThreshold = min<uint8_t>(100, lowPowerIndicatorThreshold + 10);
@@
-      lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
+      lowPowerIndicatorReactivationThreshold = min<uint8_t>(100, lowPowerIndicatorThreshold + 10);
@@
     void setLowPowerIndicatorThreshold(int8_t threshold) {
-      lowPowerIndicatorThreshold = threshold;
-      lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold + 1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
+      lowPowerIndicatorThreshold = constrain((int)threshold, 0, 100);
+      lowPowerIndicatorThreshold = autoOffEnabled
+        ? max((int)autoOffThreshold + 1, (int)lowPowerIndicatorThreshold)
+        : max(5, (int)lowPowerIndicatorThreshold);
+      lowPowerIndicatorThreshold = min((int)lowPowerIndicatorThreshold, 100);
+      lowPowerIndicatorReactivationThreshold = min(100, (int)lowPowerIndicatorThreshold + 10);
     }

Also applies to: 578-579, 650-653

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` at line 39, The code computes
lowPowerIndicatorReactivationThreshold by adding 10 to
lowPowerIndicatorThreshold without clamping, which can exceed 100 or wrap for
uint8_t; update all places that set lowPowerIndicatorReactivationThreshold (the
occurrences that use lowPowerIndicatorThreshold + 10) to first clamp
lowPowerIndicatorThreshold into a safe range (0..100) and compute the
reactivation threshold using a wider integer then cap to 100, e.g. temp =
std::min(100, std::max(0, int(lowPowerIndicatorThreshold) + 10));
lowPowerIndicatorReactivationThreshold = static_cast<uint8_t>(temp); apply the
same clamp logic wherever lowPowerIndicatorReactivationThreshold is computed.

Comment on lines +325 to +327
float dt_hours = (now - lastCoulombTime) / 3600000.0f;
lastCoulombTime = now;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reset the Coulomb integration timebase when current data is invalid.

When execution hits Line 358-Line 362, lastCoulombTime is left stale. On the next valid sample, Line 325 integrates over the entire gap using one instantaneous current reading, skewing SoC/runtime.

🔧 Suggested fix
       } else {
         estimatedTimeLeft = -1;
         smoothedCurrent = -1.0f;
         atRest = false;
+        if (coulombInitialized) lastCoulombTime = millis();
       }

Also applies to: 358-362

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 325 - 327, The code currently
integrates Coulombs using dt_hours = (now - lastCoulombTime)/3600000.0f
(variable lastCoulombTime) even when previous current samples were invalid,
causing a huge erroneous integration after a gap; modify the path that handles
invalid current samples (the block around the logic at lines 358-362) to
update/reset lastCoulombTime to the current timestamp (now) whenever you drop or
mark current data invalid so that the next valid sample uses a zero dt interval;
ensure the same reset is applied wherever current is ignored to prevent
integrating over stale gaps.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
usermods/Battery/Battery.cpp (2)

356-357: ⚠️ Potential issue | 🟠 Major

Reset Coulomb integration timebase when current data is invalid.

When current becomes invalid, lastCoulombTime stays stale. The next valid sample integrates over the full outage interval using a single instantaneous current reading.

🔧 Proposed fix
       } else {
         estimatedTimeLeft = -1;
         smoothedCurrent = -1.0f;
         atRest = false;
+        if (coulombInitialized) lastCoulombTime = millis();
       }

Also applies to: 389-393

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 356 - 357, When current data is
detected invalid, lastCoulombTime is left stale and the next valid sample will
integrate over the outage; update the code handling invalid current to reset the
Coulomb integration timebase by setting lastCoulombTime = now (the same
timestamp used to compute dt_hours) whenever you skip integration due to invalid
current so dt_hours will be computed from the next valid sample only; apply the
same fix in the other similar block (lines referencing dt_hours/lastCoulombTime
around the second integration, e.g., the block at 389-393).

39-39: ⚠️ Potential issue | 🟠 Major

Clamp low-power thresholds and derive reactivation threshold in one place.

lowPowerIndicatorReactivationThreshold can exceed 100, which makes reactivation impossible for valid level values. Clamp and recompute in the setter to keep behavior consistent.

🔧 Proposed fix
-    uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
+    uint8_t lowPowerIndicatorReactivationThreshold = min<uint8_t>(100, lowPowerIndicatorThreshold + 10);
...
-        lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
...
-      lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold + 10;
...
     void setLowPowerIndicatorThreshold(int8_t threshold) {
-      lowPowerIndicatorThreshold = threshold;
-      lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold + 1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
+      lowPowerIndicatorThreshold = constrain((int)threshold, 0, 100);
+      lowPowerIndicatorThreshold = autoOffEnabled
+        ? max((int)autoOffThreshold + 1, (int)lowPowerIndicatorThreshold)
+        : max(5, (int)lowPowerIndicatorThreshold);
+      lowPowerIndicatorThreshold = min((int)lowPowerIndicatorThreshold, 100);
+      lowPowerIndicatorReactivationThreshold = min(100, (int)lowPowerIndicatorThreshold + 10);
     }

Also applies to: 126-127, 628-629, 723-726

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` at line 39, Clamp lowPowerIndicatorThreshold to
the 0–100 range and recompute lowPowerIndicatorReactivationThreshold there
instead of ad-hoc additions: ensure the setter that assigns
lowPowerIndicatorThreshold (e.g., setLowPowerIndicatorThreshold or the function
that currently writes lowPowerIndicatorThreshold) enforces
lowPowerIndicatorThreshold = min(max(value, 0), 100) and then sets
lowPowerIndicatorReactivationThreshold = min(lowPowerIndicatorThreshold + 10,
100); replace other direct computations of
lowPowerIndicatorReactivationThreshold (the occurrences around the existing
lines with lowPowerIndicatorReactivationThreshold and any duplicates at the
other noted spots) to use the clamped value from that single setter so
reactivation never exceeds 100.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 664-667: Replace the unsafe strcpy/strcat_P assembly when building
the subscription topic with a bounded snprintf_P call: instead of using subuf,
strcpy(subuf, mqttDeviceTopic) and strcat_P(subuf, PSTR("/battery/set")), call
snprintf_P(subuf, sizeof(subuf), PSTR("%s/battery/set"), mqttDeviceTopic) and
then pass subuf to mqtt->subscribe; this keeps the same topic (matching the
existing strncmp_P logic) but prevents buffer overruns and follows the mqtt.cpp
pattern used elsewhere.

In `@usermods/Battery/readme.md`:
- Line 197: The README references the INA226 usermod inconsistently—update the
setup instructions and the custom_usermods example so they use the exact same
usermod name (either "INA226" or "INA226_v2") throughout (including the
paragraph that mentions "INA226 usermod" and the example block referenced around
lines 197 and 210-212) to prevent copy/paste misconfiguration; pick the
canonical module name used by the repository (e.g., the directory name or module
identifier) and replace the mismatched occurrences so all mentions (setup text
and custom_usermods example) are identical.

---

Duplicate comments:
In `@usermods/Battery/Battery.cpp`:
- Around line 356-357: When current data is detected invalid, lastCoulombTime is
left stale and the next valid sample will integrate over the outage; update the
code handling invalid current to reset the Coulomb integration timebase by
setting lastCoulombTime = now (the same timestamp used to compute dt_hours)
whenever you skip integration due to invalid current so dt_hours will be
computed from the next valid sample only; apply the same fix in the other
similar block (lines referencing dt_hours/lastCoulombTime around the second
integration, e.g., the block at 389-393).
- Line 39: Clamp lowPowerIndicatorThreshold to the 0–100 range and recompute
lowPowerIndicatorReactivationThreshold there instead of ad-hoc additions: ensure
the setter that assigns lowPowerIndicatorThreshold (e.g.,
setLowPowerIndicatorThreshold or the function that currently writes
lowPowerIndicatorThreshold) enforces lowPowerIndicatorThreshold = min(max(value,
0), 100) and then sets lowPowerIndicatorReactivationThreshold =
min(lowPowerIndicatorThreshold + 10, 100); replace other direct computations of
lowPowerIndicatorReactivationThreshold (the occurrences around the existing
lines with lowPowerIndicatorReactivationThreshold and any duplicates at the
other noted spots) to use the clamped value from that single setter so
reactivation never exceeds 100.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51160d4 and 519eefe.

📒 Files selected for processing (3)
  • usermods/Battery/Battery.cpp
  • usermods/Battery/battery_defaults.h
  • usermods/Battery/readme.md

Comment on lines +664 to +667
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/battery/set"));
mqtt->subscribe(subuf, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate the Battery.cpp file
find . -name "Battery.cpp" -type f | head -5

Repository: wled/WLED

Length of output: 83


🏁 Script executed:

# Read the relevant sections of Battery.cpp
if [ -f "usermods/Battery/Battery.cpp" ]; then
  echo "=== Lines 660-670 (context for lines 664-667) ==="
  sed -n '660,670p' usermods/Battery/Battery.cpp | cat -n
  echo ""
  echo "=== Lines 688-698 (context for lines 692-693) ==="
  sed -n '688,698p' usermods/Battery/Battery.cpp | cat -n
fi

Repository: wled/WLED

Length of output: 1021


🏁 Script executed:

# Get full context around subscription and topic matching
if [ -f "usermods/Battery/Battery.cpp" ]; then
  echo "=== Full context around MQTT subscription (lines 650-700) ==="
  sed -n '650,700p' usermods/Battery/Battery.cpp | cat -n
fi

Repository: wled/WLED

Length of output: 2254


🏁 Script executed:

# Search for mqttDeviceTopic definition and size constraints
rg "mqttDeviceTopic" --max-count=15 -A 2 -B 2 | head -80

Repository: wled/WLED

Length of output: 5121


🏁 Script executed:

# Check how WLED passes topics to onMqttMessage
rg "onMqttMessage" wled00/ -A 5 -B 2 | head -100

Repository: wled/WLED

Length of output: 3946


🏁 Script executed:

# Look for MQTT topic length defines
rg "MQTT.*MAX|MAX.*MQTT|MQTT.*LEN" --max-count=20

Repository: wled/WLED

Length of output: 3299


🏁 Script executed:

# Look at the full mqtt message processing flow
sed -n '200,350p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 1991


🏁 Script executed:

# Find the exact topic processing logic
rg "topicPrefixLen" wled00/mqtt.cpp -B 5 -A 10

Repository: wled/WLED

Length of output: 748


🏁 Script executed:

# Get more context on how usermods receive MQTT messages
sed -n '260,290p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 35


🏁 Script executed:

# Check if there's any path where usermods get full topic with prefix
rg "UsermodManager::onMqttMessage" wled00/mqtt.cpp -B 10 -A 5

Repository: wled/WLED

Length of output: 1084


🏁 Script executed:

# Check the exact prefix stripping logic in mqtt.cpp around line 250-330
sed -n '240,330p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 97


🏁 Script executed:

# Look at the exact topic format used in main WLED mqtt code
rg "sTopicFormat" wled00/mqtt.cpp -B 2 -A 2 | head -20

Repository: wled/WLED

Length of output: 834


🏁 Script executed:

# Get the full onMqttMessage handler implementation
sed -n '230,320p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 571


🏁 Script executed:

# Search for the static onMqttMessage function definition
rg "static void onMqttMessage" wled00/mqtt.cpp -A 80 | head -100

Repository: wled/WLED

Length of output: 2740


🏁 Script executed:

# Let me verify the exact buffer size calculation
python3 << 'EOF'
# MQTT_MAX_TOPIC_LEN default
max_topic_len = 32

# Battery usermod buffer
buffer_size = 64

# Concatenation needs: mqttDeviceTopic + "/battery/set"
# Max: 32 + strlen("/battery/set") + null terminator
# strlen("/battery/set") = 12
required = max_topic_len + 12 + 1

print(f"MQTT_MAX_TOPIC_LEN: {max_topic_len}")
print(f"Buffer size: {buffer_size}")
print(f"Required for concat: {required}")
print(f"Sufficient: {required <= buffer_size}")
EOF

Repository: wled/WLED

Length of output: 132


🏁 Script executed:

# Check if there are other usermods that handle MQTT to see the pattern
rg "onMqttMessage.*override" usermods/ -A 10 | head -60

Repository: wled/WLED

Length of output: 4320


🏁 Script executed:

# Look at the complete flow when topic doesn't match deviceTopic or groupTopic
sed -n '265,315p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 35


🏁 Script executed:

# Check if there are any other usermods implementing onMqttMessage to see the pattern
rg "bool onMqttMessage" usermods/ -A 3 | grep -A 3 "onMqttMessage.*override"

Repository: wled/WLED

Length of output: 1798


🏁 Script executed:

# Let me trace through an actual example to confirm the topic stripping behavior
# Check the exact sequence: subscription, MQTT message, and topic processing

# First verify the multi_relay implementation to see how other usermods handle this
rg "onMqttMessage.*override" usermods/multi_relay/ -A 15

Repository: wled/WLED

Length of output: 1162


🏁 Script executed:

# Find the multi_relay implementation
rg "bool onMqttMessage" usermods/multi_relay/multi_relay.cpp -A 20

Repository: wled/WLED

Length of output: 823


🏁 Script executed:

# Search for the actual implementation of onMqttMessage in multi_relay
fd "multi_relay.cpp" usermods/ -x cat {} | grep -A 20 "^bool.*onMqttMessage"

Repository: wled/WLED

Length of output: 738


🏁 Script executed:

# Let me verify the exact topic flow by examining if topics are always stripped before reaching usermods
# Check if there's a case where a usermod gets an un-stripped topic
sed -n '253,310p' wled00/mqtt.cpp | cat -n

Repository: wled/WLED

Length of output: 35


🏁 Script executed:

# Get the complete onMqttMessage function
rg "static void onMqttMessage" wled00/mqtt.cpp -A 100 | head -110

Repository: wled/WLED

Length of output: 3162


The topic matching logic is correct as-is; use snprintf_P instead of strcpy/strcat_P to avoid unsafe buffer operations.

WLED strips the mqttDeviceTopic prefix before passing topics to usermods for WLED-subscribed topics, so the usermod receives the stripped topic "/battery/set" rather than the full prefixed topic. The current strncmp_P check is appropriate. However, using strcpy and strcat_P is unnecessarily unsafe; replace with snprintf_P for clarity and consistency with WLED's codebase (see mqtt.cpp for the standard pattern).

🔧 Suggested fix
       if (mqttDeviceTopic[0] != 0) {
         char subuf[64];
-        strcpy(subuf, mqttDeviceTopic);
-        strcat_P(subuf, PSTR("/battery/set"));
+        snprintf_P(subuf, sizeof(subuf), PSTR("%s/battery/set"), mqttDeviceTopic);
         mqtt->subscribe(subuf, 0);
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/battery/set"));
mqtt->subscribe(subuf, 0);
if (mqttDeviceTopic[0] != 0) {
char subuf[64];
snprintf_P(subuf, sizeof(subuf), PSTR("%s/battery/set"), mqttDeviceTopic);
mqtt->subscribe(subuf, 0);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/Battery.cpp` around lines 664 - 667, Replace the unsafe
strcpy/strcat_P assembly when building the subscription topic with a bounded
snprintf_P call: instead of using subuf, strcpy(subuf, mqttDeviceTopic) and
strcat_P(subuf, PSTR("/battery/set")), call snprintf_P(subuf, sizeof(subuf),
PSTR("%s/battery/set"), mqttDeviceTopic) and then pass subuf to mqtt->subscribe;
this keeps the same topic (matching the existing strncmp_P logic) but prevents
buffer overruns and follows the mqtt.cpp pattern used elsewhere.


## ⏱️ Estimated Runtime

The battery usermod can estimate the remaining runtime of your project. This feature is **automatically enabled** when the [INA226 usermod](../INA226_v2/) is detected at runtime — no additional build flags or compile-time configuration are required beyond adding the INA226 usermod to `custom_usermods` (see [Setup](#setup) below).
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use the exact INA226 usermod name consistently in setup instructions.

The docs point to INA226_v2, but the custom_usermods example uses INA226. Please keep these aligned to avoid copy/paste misconfiguration.

✏️ Suggested edit
-custom_usermods = Battery INA226
+custom_usermods = Battery INA226_v2

Also applies to: 210-212

🧰 Tools
🪛 LanguageTool

[grammar] ~197-~197: Ensure spelling is correct
Context: ...> ## ⏱️ Estimated Runtime The battery usermod can estimate the remaining runtime of y...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/Battery/readme.md` at line 197, The README references the INA226
usermod inconsistently—update the setup instructions and the custom_usermods
example so they use the exact same usermod name (either "INA226" or "INA226_v2")
throughout (including the paragraph that mentions "INA226 usermod" and the
example block referenced around lines 197 and 210-212) to prevent copy/paste
misconfiguration; pick the canonical module name used by the repository (e.g.,
the directory name or module identifier) and replace the mismatched occurrences
so all mentions (setup text and custom_usermods example) are identical.

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

Labels

usermod usermod related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants