Skip to content

Conversation

@DereIBims
Copy link

@DereIBims DereIBims commented Jan 11, 2026

my usermod for the 7 Segment clock/countdown.
Viedeos and photos can be found in the Discord-channel "showcase"

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a seven-segment clock and countdown overlay that displays time on LED strips; supports clock mode (HH:MM:SS) and countdown-to-target-date modes with configurable separators and alternating display; fully configurable via JSON API and MQTT integration.
  • Documentation

    • Added comprehensive user guide with configuration instructions and library manifest for the new usermod.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

Walkthrough

Introduces a new WLED usermod that renders a six-digit, two-separator seven-segment display overlay for clock and countdown functionality. Includes time utilities, display modes, target date handling, mask management, and full integration with JSON, MQTT, and UI configuration systems.

Changes

Cohort / File(s) Summary
7-segment usermod core implementation
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h, usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
New SevenSegClockCountdown usermod class with mask management, time utilities, clock/countdown rendering logic, target validation, mode handling, LED segment drawing helpers, MQTT integration, JSON state serialization, and config persistence. Implements WLED Usermod interface with setup, loop, handleOverlayDraw, and I/O methods.
Documentation and manifest
usermods/7_seg_clock_countdown/README.md, usermods/7_seg_clock_countdown/library.json
README with feature overview, display modes, configuration guide, JSON/MQTT API examples, and customization notes. Library manifest declaring v0.1.0 project metadata.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • netmindz
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'New usermod for a 6-digit 7-Segment Clock with Countdown to date' clearly and specifically summarizes the main change: a new usermod implementing a 6-digit 7-segment display with clock and countdown functionality.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 5

🤖 Fix all issues with AI agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp:
- Around line 329-337: The current branch handling large day counts (the remDays
> 99 block using setDigitChar and setDigitInt) misrenders remDays >= 1000; add a
new branch that checks remDays >= 1000 before the remDays > 99 check and cap or
format those values (for example, display a capped "999d" or a special overflow
indicator like "OVR" or ">99d") by setting the four left digits via
setDigitChar/setDigitInt and the unit with setDigitChar('d'); ensure the logic
uses remDays >= 1000 as the new condition and leaves the existing remDays > 99
branch for 3-digit values.
- Around line 252-254: The code computes absDiff using abs(diff) which can
truncate int64_t; replace abs(diff) with a 64-bit-safe absolute call (e.g.,
llabs(diff) or std::abs<long long>(diff) / std::abs(static_cast<long
long>(diff))) so absDiff correctly holds the absolute value of the int64_t diff;
update the usage around the diff, countingUp, and absDiff variables accordingly.
- Around line 572-601: When handling MQTT updates for
targetYear/targetMonth/targetDay/targetHour/targetMinute the code sets the
individual variables but never calls validateTarget(), leaving targetUnix stale;
after each assignment to targetYear, targetMonth, targetDay, targetHour, or
targetMinute (the blocks shown) call validateTarget(true) to force recomputation
of targetUnix and related state so the countdown reflects the new values.
- Around line 552-564: Subscription and handler use different topic formats:
update SevenSegClockCountdown::onMqttConnect to subscribe with the device/topic
separator and wildcard (e.g., combine mqttDeviceTopic, a "/" separator,
MQTT_Topic and "/#") so it actually receives all 7seg subtopics, and update
SevenSegClockCountdown::onMqttMessage to check the same fully-qualified prefix
(e.g., String(mqttDeviceTopic) + "/" + MQTT_Topic) instead of "/7seg/"; ensure
both use the same separator convention and that the subscribe uses a trailing
"/#" wildcard to capture child topics.

In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.h:
- Around line 218-221: clampVal currently returns uint8_t which truncates values
>=256 (breaking year clamping for targetYear); change clampVal's return type to
int (or make it a templated function) and update its declaration/definition
(static int clampVal(int v, int lo, int hi) or template<typename T> static T
clampVal(T v, T lo, T hi)) so it preserves full-range integers, and ensure any
call sites (e.g., where clampVal is used to clamp targetYear) accept the
adjusted return type.
🧹 Nitpick comments (7)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h (3)

1-3: Consider removing standalone <stdint.h> include.

In WLED usermods, wled.h (included in the .cpp file) already provides standard integer types. The static analysis error about stdint.h not being found is a false positive in the WLED build context, but the include is likely redundant.

Suggested change
 #pragma once
-#include <stdint.h>
+// Types provided by wled.h in the implementation file

58-77: Consider computing digit bases from constants for maintainability.

The hardcoded base indices (0, 35, 80, 115, 160, 195) are correct for the default geometry but will break if LEDS_PER_SEG or SEP_LEDS changes. Per the README, users must manually update these values. Consider computing them dynamically. Based on learnings, magic numbers should be replaced with expressions using defined constants.

Example dynamic computation
static uint16_t digitBase(uint8_t d)
{
  // Layout: D0 | D1 | SEP1 | D2 | D3 | SEP2 | D4 | D5
  constexpr uint16_t D = LEDS_PER_DIGIT;  // 35
  constexpr uint16_t S = SEP_LEDS;        // 10
  switch (d)
  {
  case 0: return 0;
  case 1: return D;
  case 2: return 2*D + S;
  case 3: return 3*D + S;
  case 4: return 4*D + 2*S;
  case 5: return 5*D + 2*S;
  default: return 0;
  }
}

105-106: Typo in variable names: "Seperator" → "Separator".

SeperatorOn and SeperatorOff are misspelled. Since these names appear in JSON state, config, and MQTT topics, this typo will persist in the public API. Consider fixing before the first release.

usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp (2)

125-148: Separator logic is confusing; consider simplifying.

The current logic: SeperatorOn && SeperatorOff → blink, SeperatorOn only → on, SeperatorOff only → off, neither → blink. The naming and conditions are counterintuitive. Consider using a single enum (BLINK, ON, OFF) or renaming to separatorMode.


293-311: Consider using else if for mutually exclusive conditions.

Lines 293 and 302 have mutually exclusive conditions (absDiff < 60 and absDiff >= 60) but are separate if blocks. Using else if would make the exclusivity explicit.

usermods/7_seg_clock_countdown/README.md (2)

7-8: Use markdown link syntax for URL.

Per markdownlint, bare URLs should be formatted as links for accessibility and consistent styling.

Suggested fix
 - Active development happens in the WLED fork by DereIBims:
-  - https://github.com/DereIBims/WLED
+  - [github.com/DereIBims/WLED](https://github.com/DereIBims/WLED)

15-15: Remove spaces inside code span.

The code span ` ddd d` has leading spaces inside the backticks, which markdownlint flags. If the leading space is intentional to show display padding, consider using a non-breaking space or a clearer format.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e9dc18 and 59b6629.

📒 Files selected for processing (4)
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.h
  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/library.json
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.
Learnt from: freakintoddles2
Repo: wled/WLED PR: 4904
File: wled00/FX.cpp:3915-3930
Timestamp: 2025-09-02T19:42:57.475Z
Learning: WLED wled00/FX.cpp::mode_percent — For time-based options (% of Minute/Hour/Day), behavior is clock-like: at rollover (elapsed==0) intentionally show SEGLEN lit for one tick to represent completion, then move to 1 LED at the next second. This is by design (not a progress bar) and should not be “fixed”.
Learnt from: DedeHai
Repo: wled/WLED PR: 4923
File: wled00/FX.cpp:4883-4901
Timestamp: 2025-09-12T17:29:43.826Z
Learning: In WLED’s web UI, only one slider value (e.g., SEGMENT.intensity or SEGMENT.custom1) changes at a time; code relying on this may use simplified change guards, though presets/JSON can still update multiple fields atomically.
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.
Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.h:121-137
Timestamp: 2025-04-27T10:06:22.545Z
Learning: In the Seven Segment Display Reloaded usermod, the dimension mismatch between the default array (umSSDRNumbers[11][7]) and the override (umSSDRNumbers[11][10] = umSSDR_NUMBERS) is intentional by design, allowing for different use cases with varying numbers of segments per digit.
📚 Learning: 2025-09-02T19:42:57.475Z
Learnt from: freakintoddles2
Repo: wled/WLED PR: 4904
File: wled00/FX.cpp:3915-3930
Timestamp: 2025-09-02T19:42:57.475Z
Learning: WLED wled00/FX.cpp::mode_percent — For time-based options (% of Minute/Hour/Day), behavior is clock-like: at rollover (elapsed==0) intentionally show SEGLEN lit for one tick to represent completion, then move to 1 LED at the next second. This is by design (not a progress bar) and should not be “fixed”.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.h
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-03-29T01:22:54.617Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.h
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-11-14T13:37:30.955Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-10-20T09:38:51.997Z
Learnt from: blazoncek
Repo: wled/WLED PR: 4995
File: wled00/FX.cpp:5223-5226
Timestamp: 2025-10-20T09:38:51.997Z
Learning: WLED matrices: each dimension (SEG_W, SEG_H) is limited to ≤255; 256 or larger per side is not supported/feasible on ESP32, so effects should assume per-dimension max 255.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
📚 Learning: 2025-11-14T13:37:11.994Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-04-27T10:06:22.545Z
Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.h:121-137
Timestamp: 2025-04-27T10:06:22.545Z
Learning: In the Seven Segment Display Reloaded usermod, the dimension mismatch between the default array (umSSDRNumbers[11][7]) and the override (umSSDRNumbers[11][10] = umSSDR_NUMBERS) is intentional by design, allowing for different use cases with varying numbers of segments per digit.

Applied to files:

  • usermods/7_seg_clock_countdown/README.md
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.h
  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-04-27T09:37:28.415Z
Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.h:121-136
Timestamp: 2025-04-27T09:37:28.415Z
Learning: Using PROGMEM for the seven-segment font array (umSSDRNumbers) in the WLED SSDR usermod causes compilation problems, so it should be left as a regular array.

Applied to files:

  • usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp
📚 Learning: 2025-08-29T00:26:15.808Z
Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.h:6-14
Timestamp: 2025-08-29T00:26:15.808Z
Learning: WLED uses a vendored ArduinoJson library (version 6) located at "src/dependencies/json/ArduinoJson-v6.h" which is included through wled.h. Usermods should not directly include ArduinoJson headers but instead rely on wled.h for ArduinoJson symbols. The standard pattern is to include wled.h and use JsonObject, JsonArray, DynamicJsonDocument, etc. without additional includes.

Applied to files:

  • usermods/7_seg_clock_countdown/library.json
🧬 Code graph analysis (2)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h (1)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp (52)
  • ensureMaskSize (28-32)
  • ensureMaskSize (28-28)
  • clearMask (35-38)
  • clearMask (35-35)
  • setRangeOn (41-49)
  • setRangeOn (41-41)
  • getHundredths (53-77)
  • getHundredths (53-53)
  • drawClock (115-149)
  • drawClock (115-115)
  • revertMode (81-89)
  • revertMode (81-81)
  • SaveMode (92-100)
  • SaveMode (92-92)
  • setMode (103-111)
  • setMode (103-103)
  • drawCountdown (249-374)
  • drawCountdown (249-249)
  • setDigitInt (152-170)
  • setDigitInt (152-152)
  • setDigitChar (173-189)
  • setDigitChar (173-173)
  • setSeparator (192-197)
  • setSeparator (192-192)
  • setSeparatorHalf (200-207)
  • setSeparatorHalf (200-200)
  • applyMaskToStrip (210-219)
  • applyMaskToStrip (210-210)
  • validateTarget (223-245)
  • validateTarget (223-223)
  • setup (16-19)
  • setup (16-16)
  • loop (22-24)
  • loop (22-22)
  • handleOverlayDraw (378-400)
  • handleOverlayDraw (378-378)
  • addToJsonInfo (404-443)
  • addToJsonInfo (404-404)
  • addToJsonState (446-462)
  • addToJsonState (446-446)
  • readFromJsonState (465-510)
  • readFromJsonState (465-465)
  • addToConfig (514-528)
  • addToConfig (514-514)
  • readFromConfig (531-548)
  • readFromConfig (531-531)
  • onMqttConnect (552-557)
  • onMqttConnect (552-552)
  • onMqttMessage (560-633)
  • onMqttMessage (560-560)
  • getId (637-637)
  • getId (637-637)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp (2)
wled00/bus_manager.h (1)
  • start (79-83)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h (5)
  • digitBase (58-77)
  • LETTER_MASK (141-216)
  • sep1Base (78-78)
  • sep2Base (79-79)
  • clampVal (218-221)
🪛 Clang (14.0.6)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h

[error] 2-2: 'stdint.h' file not found

(clang-diagnostic-error)

🪛 LanguageTool
usermods/7_seg_clock_countdown/README.md

[grammar] ~10-~10: Ensure spelling is correct
Context: ... - https://github.com/DereIBims/WLED A usermod that renders a six‑digit, two‑separator...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~117-~117: Ensure spelling is correct
Context: ... enabled - showClock - showCountdown - SeperatorOn - SeperatorOff - Numeric keys (payload: integer): - tar...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~150-~150: Ensure spelling is correct
Context: ...d "false"/"0". ## UI integration - The usermod adds a compact block to the “Info” scre...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
usermods/7_seg_clock_countdown/README.md

8-8: Bare URL used

(MD034, no-bare-urls)


15-15: Spaces inside code span elements

(MD038, no-space-in-code)

🔇 Additional comments (9)
usermods/7_seg_clock_countdown/7_seg_clock_countdown.h (2)

141-215: Verify behavior for character 'x' in countdown display.

The drawCountdown() method uses setDigitChar(_, 'x') as a placeholder, but 'x' is not handled in LETTER_MASK() and falls through to the default mask (A, D, G segments lit). If this is intentional placeholder behavior, consider adding an explicit case or a comment. If 'x' should display differently (e.g., blank), add the appropriate case.


112-117: Member initialization uses localTime before it's valid.

targetYear = year(localTime) etc. are evaluated at static construction time, when localTime is likely 0 (epoch 1970). While readFromConfig() provides fallback defaults, the in-class initializers may produce unexpected values. Consider using fixed defaults (e.g., 2025) or deferring initialization.

usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp (4)

7-8: Include path uses local header correctly.

The include of "7_seg_clock_countdown.h" follows the expected pattern for WLED usermods.


53-77: LGTM: Hundredths calculation with proper clamping.

The logic correctly computes sub-second hundredths for smooth countdown display, handles both countdown and count-up modes, and clamps the result to 0–99.


378-400: Overlay draw logic is correct.

The alternating display logic using localTime / period to switch between clock and countdown is clean and functional.


637-640: Usermod registration follows WLED conventions.

The static instance and REGISTER_USERMOD macro usage follows the standard WLED usermod pattern. The hardcoded ID 0x22B8 is acceptable per project learnings about usermod IDs.

usermods/7_seg_clock_countdown/library.json (1)

10-11: Verify ESP8266 compatibility.

The manifest only targets espressif32. If this usermod also supports ESP8266, add espressif8266 to the platforms list. If ESP32-only is intentional (e.g., due to memory constraints), consider documenting this in the README.

usermods/7_seg_clock_countdown/README.md (2)

49-51: Core tweak requirement may cause maintenance burden.

Requiring users to modify wled00/FX.cpp::mode_static() to return FRAMETIME is fragile—it may be overwritten by WLED updates and requires users to maintain a patched fork. Consider documenting this as optional (hundredths may lag slightly) or exploring alternative approaches.


1-170: Comprehensive and well-structured documentation.

The README thoroughly documents panel geometry, overlay mechanism, build instructions, runtime configuration, JSON/MQTT APIs, and limitations. This is excellent for user onboarding.

Comment on lines +252 to +254
int64_t diff = (int64_t)targetUnix - (int64_t)localTime;
bool countingUp = (diff < 0);
int64_t absDiff = abs(diff);
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

Use llabs() or std::abs() for int64_t values.

abs(diff) on line 254 may truncate int64_t to int on some platforms, causing incorrect behavior for countdown differences beyond ~24 days. Use llabs(diff) or cast properly.

Proposed fix
   int64_t diff = (int64_t)targetUnix - (int64_t)localTime;
   bool countingUp = (diff < 0);
-  int64_t absDiff = abs(diff);
+  int64_t absDiff = (diff < 0) ? -diff : diff;
📝 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
int64_t diff = (int64_t)targetUnix - (int64_t)localTime;
bool countingUp = (diff < 0);
int64_t absDiff = abs(diff);
int64_t diff = (int64_t)targetUnix - (int64_t)localTime;
bool countingUp = (diff < 0);
int64_t absDiff = (diff < 0) ? -diff : diff;
🤖 Prompt for AI Agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp around lines 252 -
254, The code computes absDiff using abs(diff) which can truncate int64_t;
replace abs(diff) with a 64-bit-safe absolute call (e.g., llabs(diff) or
std::abs<long long>(diff) / std::abs(static_cast<long long>(diff))) so absDiff
correctly holds the absolute value of the int64_t diff; update the usage around
the diff, countingUp, and absDiff variables accordingly.

Comment on lines +329 to +337
if (remDays > 99)
{
setDigitChar(0, ' ');
setDigitInt(1, (remDays / 100) % 10);
setDigitInt(2, (remDays / 10) % 10);
setDigitInt(3, remDays % 10);
setDigitChar(4, 'd');
setDigitChar(5, ' ');
return;
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

Display overflow for ≥1000 days remaining.

When remDays >= 1000, the display shows incorrect digits (e.g., 1000 days displays as "000 d"). Consider adding a branch for 1000+ days or capping the display.

🤖 Prompt for AI Agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp around lines 329 -
337, The current branch handling large day counts (the remDays > 99 block using
setDigitChar and setDigitInt) misrenders remDays >= 1000; add a new branch that
checks remDays >= 1000 before the remDays > 99 check and cap or format those
values (for example, display a capped "999d" or a special overflow indicator
like "OVR" or ">99d") by setting the four left digits via
setDigitChar/setDigitInt and the unit with setDigitChar('d'); ensure the logic
uses remDays >= 1000 as the new condition and leaves the existing remDays > 99
branch for 3-digit values.

Comment on lines +552 to +564
void SevenSegClockCountdown::onMqttConnect(bool sessionPresent)
{
String topic = mqttDeviceTopic;
topic += MQTT_Topic;
mqtt->subscribe(topic.c_str(), 0);
}

// Handle usermod MQTT messages (simple key/value).
bool SevenSegClockCountdown::onMqttMessage(char *topic, char *payload)
{
String topicStr = String(topic);
if (!topicStr.startsWith(F("/7seg/")))
return false;
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

MQTT topic mismatch: subscription vs. message handling.

onMqttConnect subscribes to "{mqttDeviceTopic}7seg" (e.g., wled/mystrip7seg), but onMqttMessage checks for topics starting with "/7seg/". This mismatch means MQTT messages won't be handled. The subscription should include a separator and wildcard, and the message parsing should match.

Proposed fix
 void SevenSegClockCountdown::onMqttConnect(bool sessionPresent)
 {
   String topic = mqttDeviceTopic;
-  topic += MQTT_Topic;
+  topic += "/";
+  topic += MQTT_Topic;
+  topic += "/#";  // Subscribe to all subtopics
   mqtt->subscribe(topic.c_str(), 0);
 }

 bool SevenSegClockCountdown::onMqttMessage(char *topic, char *payload)
 {
   String topicStr = String(topic);
-  if (!topicStr.startsWith(F("/7seg/")))
+  // Expected format: {mqttDeviceTopic}/7seg/{key}
+  String prefix = String(mqttDeviceTopic) + "/7seg/";
+  if (!topicStr.startsWith(prefix))
     return false;
-  String subTopic = topicStr.substring(strlen("/7seg/"));
+  String subTopic = topicStr.substring(prefix.length());
🤖 Prompt for AI Agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp around lines 552 -
564, Subscription and handler use different topic formats: update
SevenSegClockCountdown::onMqttConnect to subscribe with the device/topic
separator and wildcard (e.g., combine mqttDeviceTopic, a "/" separator,
MQTT_Topic and "/#") so it actually receives all 7seg subtopics, and update
SevenSegClockCountdown::onMqttMessage to check the same fully-qualified prefix
(e.g., String(mqttDeviceTopic) + "/" + MQTT_Topic) instead of "/7seg/"; ensure
both use the same separator convention and that the subscribe uses a trailing
"/#" wildcard to capture child topics.

Comment on lines +572 to +601
if (subTopic.indexOf(F("targetYear")) >= 0)
{
String payloadStr = String(payload);
targetYear = payloadStr.toInt();
return true;
}
if (subTopic.indexOf(F("targetMonth")) >= 0)
{
String payloadStr = String(payload);
targetMonth = (uint8_t)payloadStr.toInt();
return true;
}
if (subTopic.indexOf(F("targetDay")) >= 0)
{
String payloadStr = String(payload);
targetDay = (uint8_t)payloadStr.toInt();
return true;
}
if (subTopic.indexOf(F("targetHour")) >= 0)
{
String payloadStr = String(payload);
targetHour = (uint8_t)payloadStr.toInt();
return true;
}
if (subTopic.indexOf(F("targetMinute")) >= 0)
{
String payloadStr = String(payload);
targetMinute = (uint8_t)payloadStr.toInt();
return true;
}
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

MQTT target updates don't call validateTarget() to recompute targetUnix.

When target fields (year, month, day, hour, minute) are updated via MQTT, validateTarget() is never called, so targetUnix remains stale and the countdown shows incorrect times.

Proposed fix

Add a call to validateTarget(true) after updating any target field:

   if (subTopic.indexOf(F("targetYear")) >= 0)
   {
     String payloadStr = String(payload);
     targetYear = payloadStr.toInt();
+    validateTarget(true);
     return true;
   }
   if (subTopic.indexOf(F("targetMonth")) >= 0)
   {
     String payloadStr = String(payload);
     targetMonth = (uint8_t)payloadStr.toInt();
+    validateTarget(true);
     return true;
   }
   // ... similarly for targetDay, targetHour, targetMinute
🤖 Prompt for AI Agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.cpp around lines 572 -
601, When handling MQTT updates for
targetYear/targetMonth/targetDay/targetHour/targetMinute the code sets the
individual variables but never calls validateTarget(), leaving targetUnix stale;
after each assignment to targetYear, targetMonth, targetDay, targetHour, or
targetMinute (the blocks shown) call validateTarget(true) to force recomputation
of targetUnix and related state so the countdown reflects the new values.

Comment on lines +218 to +221
static uint8_t clampVal(int v, int lo, int hi)
{
return (v < lo) ? lo : (v > hi ? hi : v);
}
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 | 🔴 Critical

Fix return type truncation for year validation.

clampVal returns uint8_t, but it's used to clamp targetYear (range 1970–2099). This causes truncation, e.g., clampVal(2025, 1970, 2099) returns (uint8_t)2025 = 233 instead of 2025. Change the return type to int or use a template.

Proposed fix
-  static uint8_t clampVal(int v, int lo, int hi)
+  static int clampVal(int v, int lo, int hi)
   {
-    return (v < lo) ? lo : (v > hi ? hi : v);
+    return (v < lo) ? lo : ((v > hi) ? hi : v);
   }
📝 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
static uint8_t clampVal(int v, int lo, int hi)
{
return (v < lo) ? lo : (v > hi ? hi : v);
}
static int clampVal(int v, int lo, int hi)
{
return (v < lo) ? lo : ((v > hi) ? hi : v);
}
🤖 Prompt for AI Agents
In @usermods/7_seg_clock_countdown/7_seg_clock_countdown.h around lines 218 -
221, clampVal currently returns uint8_t which truncates values >=256 (breaking
year clamping for targetYear); change clampVal's return type to int (or make it
a templated function) and update its declaration/definition (static int
clampVal(int v, int lo, int hi) or template<typename T> static T clampVal(T v, T
lo, T hi)) so it preserves full-range integers, and ensure any call sites (e.g.,
where clampVal is used to clamp targetYear) accept the adjusted return type.

@DedeHai
Copy link
Collaborator

DedeHai commented Jan 12, 2026

thanks for contributing.
so is this higly specific to your hardware? Did you consider extending the existing 7-segment usermod instead of creating a third one?

@DereIBims
Copy link
Author

To be honest I didn't consider extending the existing mod.
However the mod is not hardware specific.
Everything important (like the led count per segment or the segment order and position) can easily be adjusted in the header file.

But no problem if you don't want to merge it to the official code base, then I'll just keep it available in my fork :)

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.

2 participants