Skip to content

Preset Navigation usermod#4354

Closed
obar wants to merge 1 commit intowled:mainfrom
obar:usermod-preset-array
Closed

Preset Navigation usermod#4354
obar wants to merge 1 commit intowled:mainfrom
obar:usermod-preset-array

Conversation

@obar
Copy link
Copy Markdown

@obar obar commented Dec 6, 2024

A simple usermod called Preset Array Preset Navigation (edit: it occurred to me since making the request, this name could be better! And this is my only chance to fix it! Sorry if anyone was in the midst of dealing with this while I made the change). It lets you group effect presets together however you see fit, and cycle between the groups ("banks") without going through every single effect preset you've saved. When you've gotten to the bank you want, you can then cycle through just the presets of that bank.

This is primarily focused for controlling WLED in ways other than the web UI.

Preset banks are one one line of preset IDs, comma separated:

Screenshot 2024-12-06 at 16-04-47 Usermod Settings

Cycling between banks in this example takes you through preset 10, 20, 30, 40, then back to 10. If you are at 30, cycling within the bank takes you to 31, 32, 33, and then back to 30.

For a specific example: presets in a bank can have any logical connection you want, in this video demo of the usermod I show how the third bank (presets 30,31,32,33) are all different color variations of a flame effect. When cycling between banks you don't have to cycle through every color variation if you aren't going for flame. When you do get to that flame effect, you can cycle just within that bank through the color variations.

Another benefit of this usermod over normal preset cycling is that it isn't sequence-sensitive. I've used a numerical pattern above but a new preset can be squeezed in anywhere in the cycle, whereas the typical preset cycling API call needs preset IDs to be in order without gaps.

This usermod uses a textarea in the config for multiline input, so it depends on #4217

Summary by CodeRabbit

  • New Features
    • Introduced a "Preset Navigation" usermod for organizing effect presets into user-defined banks and navigating them easily, ideal for non-touchscreen interfaces.
    • Users can cycle through banks and presets with both wrapping and non-wrapping commands via the JSON API.
    • Supports configuration of banks and presets, with customizable limits.
  • Documentation
    • Added detailed usage instructions and configuration guidelines for the new "Preset Navigation" usermod.

Allows grouping of related effects presets in 'banks' as the user likes,
and provides navigation functions to traverse between or within banks.
@obar obar force-pushed the usermod-preset-array branch from 9b2cea5 to a868e63 Compare December 7, 2024 04:19
@obar obar changed the title Preset Array usermod Preset Navigation usermod Dec 7, 2024
@obar
Copy link
Copy Markdown
Author

obar commented Dec 7, 2024

Ninja edit: Preset Navigation is a better name for this. Updated accordingly but the usermod is the same as described.

@blazoncek
Copy link
Copy Markdown
Contributor

IMO this is why playlists exist.

@obar
Copy link
Copy Markdown
Author

obar commented Dec 7, 2024

Thanks for mentioning it. Can you say more about that? It wouldn't be the first time my mental model about WLED would be wrong ;)

@softhack007
Copy link
Copy Markdown
Member

@obar is this a revival of the "textarea" mod #4217 ?
I think we already decided to not add large textarea support.

@softhack007 softhack007 added discussion usermod usermod related labels Dec 11, 2024
@obar
Copy link
Copy Markdown
Author

obar commented Dec 11, 2024

@softhack007 Not a revival of anything, it's a feature to let a user create a menu of many presets.

There were concerns about textareas in the usermods page initially voiced but they were all addressed, and nobody has suggested any further issues since.

@blazoncek
Copy link
Copy Markdown
Contributor

Handling presets is not a thing usermod would need to be invented for IMO.
If anything it should be a core feature.

@obar
Copy link
Copy Markdown
Author

obar commented Dec 13, 2024

I could adapt most of this code for a core feature.

If WLED were desktop-first, I'd think about a drag-and-drop reordering interface for the preset cards themselves... but that demands a lot of screen real estate, and drag-to-reorder implementations are only barely good enough for a single list on mobile—this is much more complicated as a list of lists . A text area was the simplest idea that I could think up. An enhancement might be a JS parser to check for properly formed input/no undefined preset IDs.

Should a text area like this live in a heading under Macros, or would it be a better fit under User Interface?

@netmindz netmindz changed the base branch from 0_15 to main December 16, 2024 13:22
@obar
Copy link
Copy Markdown
Author

obar commented Dec 18, 2024

I think the utility is there for the users who change their WLED effects with buttons, a remote control, an encoder, or other interesting interfaces that aren't web. Not every user but also not the tiniest minority either. Fortunately it won't add much to the binary.

For now, how about I do this: text area for a preset array under Macros (because it's fundamentally a macro functionality), no JS input checking but still use the C++ input checking shown in my branch, and write it up for the docs. In the future this feature can get fancier as needed, or even move places.

Sounds good?

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Dec 18, 2024

I like the idea, I just fail see the difference between this and a playlist. can you elaborate?

@blazoncek
Copy link
Copy Markdown
Contributor

All of the OP requests can (as already mentioned) be achieved by carefully crafted presets, playlists and button/IR assignments.
Please see KB and sources (json.cpp) as KB may lack complete details for information on JSON API.
I see no benefit in implementing this PR.

@obar
Copy link
Copy Markdown
Author

obar commented Dec 19, 2024

Sorry team, I do try to look at internet messages generously but I'm struggling to view this discussion and the related one on textareas as something other than animosity. I've answered challenge questions to no response, and I've asked questions that sometimes don't get a response either. I feel like I've caught some snark, but it might just be cultural differences in communication. In the defense of you all, obviously this isn't the only issue or feature being discussed or worked on, and I know you all do a lot to support complete strangers with projects: often with the very basics of how to wire things up. That's incredibly generous. Just noting how this specific discussion feels to me. I did ask how this feature is the same as playlists when it was brought up. The reply I eventually got to that question was that preset handling should be a core feature rather than a usermod, not that it already exists as a core feature. That's why I went forward with asking how this could best fit in.

WLED is an open source project of course, and nobody owes anyone anything in open source, but perhaps if someone looks like they're willing to be a new contributor it's worth meeting them with extra kindness and patience, understanding that they aren't as knowledgeable about the code base as seasoned contributors. Anyway, that's a side suggestion and I'd be excited to put it behind, start fresh.


@DedeHai that's a great way to put it in a question. My understanding of the distinctions follow.

A playlist is a 1D list, and you can navigate it in one direction: "np": "t". This proposed interface is a 2D list-of-lists, where you can navigate in 4 directions:

"pn": "b+"
"pn": "b-"
"pn": "p+"
"pn": "p-"

A playlist is composed in a largely linear way: it's possible to delete or change single entries anywhere in the list, but there isn't rearranging and you can only insert at the end, not at the beginning or middle. This proposed interface makes it very easy to rearrange the banks and the presets within the banks.

I believe most people use a playlist to automatically cycle their LEDs through a set of effects. This interface lets you build a menu of effects where the input is likely tactile and the graphical feedback of where you are in the menu is the LED state.

To me they aren't the same feature. But as I said before: I'd love to hear more, my understanding could be flawed. Thanks for your time!

@willmmiles
Copy link
Copy Markdown
Member

Hi @obar,

I think the key contentions are:

  • We feel that there should be one and exactly one abstraction in WLED for defining a list of presets: a playlist. We do not want a second interface for entering a list of presets. As I understand things, you've identified two weaknesses in the playlist implementation as they pertain to your objectives:

    • There's no "previous entry in playlist" operation. I think this is also a weakness and I'd be happy to consider a patch that adds this feature; it does need some thought as to how to handle shuffling, but that's a solvable problem. Note that the 'next preset in playlist' API feature is itself quite recent (Adds an API parameter to allow the user to skip to the next preset in a playlist at any time #3946)!
    • The web UI doesn't provide support for easily re-ordering entries in a playlist. This is a definite gap. I have a vague recollection that someone else had put some effort towards this feature, though I can't find the reference here. This would also be a valuable feature in and of itself.
  • Playlists are, themselves, presets. We feel there should be one and exactly one abstraction in WLED for defining a list of presets: a playlist. It is my understanding that a playlist that is a "list of playlists" can already be expressed in the presets.json file. Again, though, there are indeed missing features to get to the behaviour you'd like:

    • Playlists would need to be able to remember their previous state, eg. that preset 17 last loaded index 5 of its playlist. I think this would also be a useful feature everywhere, independent of your specific use case. I'd probably suggest making it opt-in for each playlist ('resume when reloaded', etc.). Once this is available, "ps": 17, "np": "t" could be composed to implement "next bank" as you describe.
    • There is no web UI support for constructing a playlist of playlists (as it wouldn't be very useful right now). With the above features, this could be unlocked.
    • Some care must be taken to clarify that the current implementation would support only the "innermost" playlist's auto-cycling behaviour -- the timing properties of meta-playlists would be ignored. A "playlist stack" feature could also be implemented, though it probably isn't necessary for your use case.

The interesting point to me is that each one of those features would be a valuable addition on its own -- and then together they combine to make something even greater.

I'm sorry it's been a challenge to get a clear direction - we're all working on this in our spare time. Does this provide more clarity as to a path forward?

@obar
Copy link
Copy Markdown
Author

obar commented Dec 19, 2024

Thanks for clarifying some things @willmmiles. I think the biggest point that hadn't been conveyed to me was this:

We feel that there should be one and exactly one abstraction in WLED for defining a list of presets: a playlist.

So it's not so much that what I was proposing === playlists, but rather that because it contains multiple presets it should be a playlist.

You've outlined a great list of changes that need to happen to generalize playlists further, and would allow this interface proposal to be reproduced in the playlists model.

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Dec 20, 2024

@obar your contribution is much appreciated, maybe there was some misunderstanding/communication breakdown.
Regarding this UM vs playlists I agree with @willmmiles that instead of adding a new feature on top to patch current shortcomings it would be better to improve the playlists and preset handling in general towards a more "natural" user interaction, which I believe should not be editing fields of numbers but rather moving items in a list. Your approach however does a great job in showing the benefits of improved preset handling, the option to re-order or make nested playlists.

@blazoncek
Copy link
Copy Markdown
Contributor

  • There is no web UI support for constructing a playlist of playlists (as it wouldn't be very useful right now). With the above features, this could be unlocked.

I already made that. The problem, as you already mentioned, is how long does a nested playlist run? Until it is finished or until parent timer runs out? Currently implemented as a 1st option.

@github-actions
Copy link
Copy Markdown

Hey! This pull request has been open for quite some time without any new comments now. It will be closed automatically in a week if no further activity occurs.
Thank you for contributing to WLED! ❤️

@github-actions github-actions bot added the stale This issue will be closed soon because of prolonged inactivity label Apr 19, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 19, 2025

Walkthrough

This update introduces a new usermod called "Preset Navigation" for the WLED project. The core logic is implemented in a new header file that defines a class for managing navigation through user-defined banks of effect presets, with support for JSON API commands and configuration persistence. Documentation for the usermod is provided in a new README file, detailing setup, usage, and configuration instructions. Integration with the WLED usermod system is achieved by conditionally including and registering the new usermod in the usermods list source file, based on a build flag.

Changes

File(s) Change Summary
usermods/preset_navigation/preset_navigation.h Added a new header file defining the UsermodPresetNavigation class, which manages preset navigation via banks and supports JSON-based configuration and state commands. Implements methods for cycling through banks and presets, parsing configuration, and applying presets.
usermods/preset_navigation/readme.md Added documentation for the Preset Navigation usermod, including setup instructions, usage details, API commands, and configuration guidance. No code or public entity changes.
wled00/usermods_list.cpp Modified to conditionally include and register the new UsermodPresetNavigation usermod when the USERMOD_PRESET_NAVIGATION macro is defined. This includes a new header inclusion and an instantiation/registration line in the usermod registration function.
✨ 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (15)
usermods/preset_navigation/preset_navigation.h (9)

13-18: Consider potential overflow with uint8_t for preset arrays.

Using uint8_t for the preset arrays limits preset IDs to 0-255. While this is likely sufficient for most use cases, it might be worth documenting this limitation in the comments or README.


22-24: Improve variable naming for better readability.

The variables x, y, and xMax would benefit from more descriptive names that indicate their purpose, such as currentBankIndex, currentPresetIndex, and maxBankIndex.

-    int x = 0; // x is current bank
-    int y = 0; // y is current sub-bank preset
-    int xMax = 0;
+    int currentBankIndex = 0; // Index of the current bank
+    int currentPresetIndex = 0; // Index of the current preset within the bank
+    int maxBankIndex = 0; // Maximum bank index

19-19: Unused variable detected.

The variable bootupPresetNavigation is declared but never used in the code. Consider removing it if not needed or implementing its intended functionality.

-    String bootupPresetNavigation;

42-45: Remove unnecessary return statement and consider applying initial preset.

The setup() method contains an unnecessary return statement (as it's a void function) and doesn't apply the initial preset.

-    void setup() override {
-      x = y = 0;
-      return;
-    }
+    void setup() override {
+      x = y = 0;
+      if (enabled && readArray() && xMax >= 0) {
+        applyPresetNavigation();
+      }
+    }

47-49: Remove unnecessary return statement.

The loop() method contains an unnecessary return statement.

-    void loop() override {
-      return;
-    }
+    void loop() override {
+      // No operation needed in loop
+    }

101-148: Add validation for preset IDs and improve error handling.

The readArray() method could benefit from validating that the parsed preset IDs correspond to valid presets in the system. Additionally, the function always returns true even if there might be errors during parsing.

Consider modifying the return value to indicate success or failure and adding preset validation:

 bool UsermodPresetNavigation::readArray() {
   int readX = 0;
   int i = 0;
   int cursor = 0;
   int currentNewline = presetArray.indexOf("\n");
   int currentComma;
   bool finished = false;
   bool bankFinished;
+  bool success = true;

   while (!finished) {
     // Iterate over each bank (line of text) in presetArray (string representation of array)
     if (currentNewline == -1){
       finished = true;
       currentNewline = presetArray.length();
     }
     if (readX >= USERMOD_PRESET_NAVIGATION_MAX_BANKS) {
       DEBUG_PRINTLN("Preset navigation: exceeded max banks");
+      success = false;
       break;
     }
     currentComma = presetArray.indexOf(",", cursor);
     bankFinished = false;
     bankOffset[readX]=i;
     while (!bankFinished) {
       // Iterate through each PRESET (comma separated value) in this bank
       if (i >= USERMOD_PRESET_NAVIGATION_MAX_PRESETS) {
         DEBUG_PRINTLN("Preset navigation: exceeded max presets");
+        success = false;
         break;
       }
       if (currentComma == -1 || currentComma > currentNewline) {
         bankFinished = true;
         currentComma = currentNewline;
       }
       String preset = presetArray.substring(cursor,currentComma);
       presets[i] = preset.toInt();
+      // Optional: Validate that the preset ID exists in the system
+      // if (!presetExists(presets[i])) {
+      //   DEBUG_PRINTF("Preset navigation: preset %d does not exist\n", presets[i]);
+      // }
       i++;
       cursor = currentComma + 1;
       currentComma = presetArray.indexOf(",", cursor);
     } // end PRESET loop
     readX++;
     // Find our next newline
     cursor = currentNewline + 1;
     currentNewline = presetArray.indexOf("\n", cursor);
   } // end bank loop

   xMax=readX-1;
   bankOffset[readX]=i;
-  return true;
+  return success;
 }

150-152: Implement or remove empty method.

The printArray() method is declared but has an empty implementation. Either implement it for debugging purposes or remove it if not needed.

-void UsermodPresetNavigation::printArray() {
-
-}
+// If needed for debugging
+void UsermodPresetNavigation::printArray() {
+  DEBUG_PRINTLN("Preset Navigation Banks:");
+  for (int i = 0; i <= xMax; i++) {
+    DEBUG_PRINTF("Bank %d (%d presets): ", i, bankOffset[i+1] - bankOffset[i]);
+    for (int j = bankOffset[i]; j < bankOffset[i+1]; j++) {
+      DEBUG_PRINTF("%d ", presets[j]);
+    }
+    DEBUG_PRINTLN();
+  }
+}

154-172: Consider returning a success indicator for navigation methods.

The incBank and decBank methods don't indicate whether the navigation was successful. Consider returning a boolean to allow callers to know if the operation had an effect.

-void UsermodPresetNavigation::incBank(bool wrap){
+bool UsermodPresetNavigation::incBank(bool wrap){
   if (x == xMax && wrap) {
     x = 0; y = 0;
     applyPresetNavigation();
+    return true;
   } else if (x < xMax) {
     x++; y = 0;
     applyPresetNavigation();
+    return true;
   }
+  return false;
 }

-void UsermodPresetNavigation::decBank(bool wrap){
+bool UsermodPresetNavigation::decBank(bool wrap){
   if (x == 0 && wrap) {
     x = xMax; y = 0;
     applyPresetNavigation();
+    return true;
   } else if (x > 0) {
     x--; y = 0;
     applyPresetNavigation();
+    return true;
   }
+  return false;
 }

174-198: Add feedback for "no cycle" conditions in preset navigation.

The incPreset and decPreset methods silently do nothing when there's nowhere to cycle. Consider providing feedback or returning a success indicator.

-void UsermodPresetNavigation::incPreset(bool wrap){
+bool UsermodPresetNavigation::incPreset(bool wrap){
   int yMax = bankOffset[x+1] - bankOffset[x] - 1;
   if (yMax == 0) {
     // We don't have anywhere to cycle
+    DEBUG_PRINTLN("Preset navigation: bank has only one preset");
+    return false;
   } else if (y == yMax && wrap) {
     y=0;
     applyPresetNavigation();
+    return true;
   } else if (y < yMax) {
     y++;
     applyPresetNavigation();
+    return true;
   }
+  return false;
 }

-void UsermodPresetNavigation::decPreset(bool wrap){
+bool UsermodPresetNavigation::decPreset(bool wrap){
   int yMax = bankOffset[x+1] - bankOffset[x] - 1;
   if (yMax == 0) {
     // We don't have anywhere to cycle
+    DEBUG_PRINTLN("Preset navigation: bank has only one preset");
+    return false;
   } else if (y == 0 && wrap) {
     y = yMax;
     applyPresetNavigation();
+    return true;
   } else if (y > 0) {
     y--;
     applyPresetNavigation();
+    return true;
   }
+  return false;
 }
usermods/preset_navigation/readme.md (6)

11-21: Document the preset ID limitation.

The preset notation explanation is clear, but it doesn't mention the limitation of preset IDs to 0-255 due to the use of uint8_t in the implementation.

Add a note about the preset ID limitation:

 This is assuming you've defined presets for given effects with the ID numbers 10, 11, 12, 21... etc. The preset IDs do not have to be sequential or follow any pattern like this, but it's up to you to carefully input this information: the usermod currently doesn't do any checks here for undefined preset IDs. **Do not include trailing commas after banks, just a newline**. You can include a space after commas, but that just makes the stored string take up a little more space in memory.
+
+Note that preset IDs must be between 0 and 255 due to implementation constraints.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

14-14: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


14-18: Improve code block formatting.

The fenced code block should have a language specified for proper syntax highlighting.

-```
+```text
 10,11,12
 20,21
 30,31,32,33

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

14-14: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`22-24`: **Clarify initial state on startup.**

The example states that "After starting up WLED, the state will be Preset 10", but this might not be the case if the user configures a different first preset in their first bank. Consider clarifying this.

```diff
-After starting up WLED, the state will be Preset 10---you can set this preset to be applied at boot when creating it. One option is to cycle through banks. Cycling up will apply Preset 20, and if cycling up again, Preset 30. If a wrapping command is used, then cycling up a third time will cycle back to Preset 10. From Preset 10, the other option is to cycle within a bank. Cycling up will apply Preset 11 next. Cycling down with a preset wrapping command from 10 will apply Preset 12.
+After starting up WLED, the state will begin with the first preset in your first bank (Preset 10 in our example). You can set this preset to be applied at boot when creating it. One option is to cycle through banks. Cycling up will apply the first preset in the next bank (Preset 20), and if cycling up again, the first preset in the third bank (Preset 30). If a wrapping command is used, then cycling up a third time will cycle back to the first preset in the first bank (Preset 10). From Preset 10, the other option is to cycle within a bank. Cycling up will apply Preset 11 next. Cycling down with a preset wrapping command from 10 will apply Preset 12.
🧰 Tools
🪛 LanguageTool

[grammar] ~23-~23: Before the countable noun ‘at’ an article or a possessive pronoun is necessary.
Context: ...ou can set this preset to be applied at boot when creating it. One option is to cycl...

(IN_NN_CC_VBG)


26-35: Add examples of programmatic API command usage.

The API commands are clearly explained, but examples of how to send these commands programmatically (e.g., via HTTP requests) would be helpful.

Add an example of sending commands programmatically:

 | `p~-`   | Decrement preset with wrap around |
 | `p-`    | Decrement preset without wrapping |

+### Example of sending commands via HTTP
+
+You can send these commands via an HTTP request to the JSON API endpoint:
+
+```
+http://[WLED-IP]/json?pn=b~
+```
+
+Or using a POST request with JSON payload:
+
+```json
+{
+  "pn": "b~"
+}
+```

43-45: Clearer explanation of state tracking limitations.

The explanation of state tracking limitations could be clearer with an explicit example.

-This usermod naively keeps track of the current position in the navigation array. If you use the navigation commands to get to Preset 20 in our example above, and then use the web interface to apply Preset 10, sending an Cycle Bank Up command will still go to Preset 30 as if the previous state was still 20.
+This usermod independently keeps track of its own navigation state. This means it's unaware of preset changes made through other interfaces. For example, if you:
+1. Use the navigation commands to go to Preset 20 (first preset in bank 2)
+2. Then use the web interface to manually apply Preset 10 (first preset in bank 1)
+3. Then send a "Cycle Bank Up" command
+
+The usermod will apply Preset 30 (first preset in bank 3), not Preset 20, because it still thinks you're in bank 2. The usermod doesn't automatically sync its state with manual preset changes.
🧰 Tools
🪛 LanguageTool

[misspelling] ~44-~44: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...b interface to apply Preset 10, sending an Cycle Bank Up command will still go to ...

(EN_A_VS_AN)


[grammar] ~44-~44: You can use the subjunctive mood in this context.
Context: ...o to Preset 30 as if the previous state was still 20. I like to save presets with ...

(SUBJUNCTIVE_MOOD)


46-46: Make personal preference section clearer.

The note about personal preference regarding brightness settings could be formatted more clearly as a separate section or tip.

-I like to save presets with "include brightness" unchecked, so that changing the brightness is independent to the changing of effects. In fact, on my own WLED builds I change the default of that checkbox to unchecked, by editing the file `wled00/data/index.js` and rebuilding the web interface with npm before compiling.
+## Tip: Handling Brightness
+
+I personally prefer to save presets with "include brightness" unchecked, so that changing the brightness is independent of changing effects. This allows you to adjust brightness without affecting your preset navigation.
+
+Note: On my own WLED builds, I change the default of that checkbox to unchecked by editing `wled00/data/index.js` and rebuilding the web interface with npm before compiling.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~46-~46: The preposition “of” seems more likely in this position.
Context: ... changing the brightness is independent to the changing of effects. In fact, on my...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18e0ec9 and a868e63.

⛔ Files ignored due to path filters (2)
  • usermods/preset_navigation/PresetNavigationDiagram.png is excluded by !**/*.png
  • usermods/preset_navigation/ScreenshotBankUp.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • usermods/preset_navigation/preset_navigation.h (1 hunks)
  • usermods/preset_navigation/readme.md (1 hunks)
  • wled00/usermods_list.cpp (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
wled00/usermods_list.cpp (1)
wled00/um_manager.cpp (2)
  • add (65-70)
  • add (65-65)
🪛 LanguageTool
usermods/preset_navigation/readme.md

[style] ~2-~2: To reduce wordiness, try specifying a number or using “many” or “numerous” instead.
Context: ... usermod This usermod lets you navigate a large number of effect presets more easily. You can gro...

(LARGE_NUMBER_OF)


[uncategorized] ~2-~2: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...* that you can navigate either between (eg bank 1 to bank 2) or within (switch bet...

(E_G)


[grammar] ~23-~23: Before the countable noun ‘at’ an article or a possessive pronoun is necessary.
Context: ...ou can set this preset to be applied at boot when creating it. One option is to cycl...

(IN_NN_CC_VBG)


[uncategorized] ~41-~41: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...at you added this usermod to the build, eg `-D USERMOD_PRESET_NAVIGATION_MAX_PRESE...

(E_G)


[misspelling] ~44-~44: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...b interface to apply Preset 10, sending an Cycle Bank Up command will still go to ...

(EN_A_VS_AN)


[grammar] ~44-~44: You can use the subjunctive mood in this context.
Context: ...o to Preset 30 as if the previous state was still 20. I like to save presets with ...

(SUBJUNCTIVE_MOOD)


[uncategorized] ~46-~46: The preposition “of” seems more likely in this position.
Context: ... changing the brightness is independent to the changing of effects. In fact, on my...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION)

🪛 markdownlint-cli2 (0.17.2)
usermods/preset_navigation/readme.md

14-14: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🔇 Additional comments (8)
usermods/preset_navigation/preset_navigation.h (4)

5-11: Good use of configurable constants with sensible defaults.

The use of preprocessor directives to define configurable constants with reasonable defaults is a good practice. This allows users to override these values as needed without modifying the source code.


51-65: Command handling looks good with clear structure.

The command handling in readFromJsonState is well-implemented with clear structure and comprehensive command options.


73-93: The config parsing and carriage return handling is thorough.

Good implementation of configuration handling with attention to detail in removing carriage returns for platform independence.


97-99: Clear naming and helpful comment about textarea.

The static constants for naming have clear names, and the comment about the trailing ">" for textarea is helpful for understanding the UI integration.

wled00/usermods_list.cpp (2)

245-247: Proper conditional include for the new usermod.

The include directive for the new usermod follows the established pattern in the file, correctly using the USERMOD_PRESET_NAVIGATION flag.


478-480: Correct registration of the usermod in the manager.

The registration of the UsermodPresetNavigation class in the registerUsermods() function is properly implemented following the pattern used for other usermods.

usermods/preset_navigation/readme.md (2)

1-7: Clear introduction with helpful visual.

The introduction clearly explains the purpose and benefits of the usermod. The visual diagram is particularly helpful for understanding the concept of banks and presets.

🧰 Tools
🪛 LanguageTool

[style] ~2-~2: To reduce wordiness, try specifying a number or using “many” or “numerous” instead.
Context: ... usermod This usermod lets you navigate a large number of effect presets more easily. You can gro...

(LARGE_NUMBER_OF)


[uncategorized] ~2-~2: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...* that you can navigate either between (eg bank 1 to bank 2) or within (switch bet...

(E_G)


8-10: Concise setup instructions with link to documentation.

The setup instructions are clear and concise, with a helpful link to the WLED compilation documentation for users who need more information.

Comment on lines +26 to +31
// One day might return false on a parsing error
bool readArray();
void printArray();
inline void applyPresetNavigation(){
applyPreset(presets[bankOffset[x]+y]);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add bounds checking to prevent potential out-of-bounds access.

The applyPresetNavigation() method lacks bounds checking before accessing the arrays. This could lead to out-of-bounds access if navigation state becomes invalid.

-    inline void applyPresetNavigation(){
-      applyPreset(presets[bankOffset[x]+y]);
-    }
+    inline void applyPresetNavigation(){
+      if (x >= 0 && x <= xMax && bankOffset[x] + y < USERMOD_PRESET_NAVIGATION_MAX_PRESETS) {
+        applyPreset(presets[bankOffset[x]+y]);
+      } else {
+        DEBUG_PRINTLN("Preset navigation: invalid indices");
+      }
+    }
📝 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
// One day might return false on a parsing error
bool readArray();
void printArray();
inline void applyPresetNavigation(){
applyPreset(presets[bankOffset[x]+y]);
}
// One day might return false on a parsing error
bool readArray();
void printArray();
inline void applyPresetNavigation(){
if (x >= 0 && x <= xMax && bankOffset[x] + y < USERMOD_PRESET_NAVIGATION_MAX_PRESETS) {
applyPreset(presets[bankOffset[x] + y]);
} else {
DEBUG_PRINTLN("Preset navigation: invalid indices");
}
}

@github-actions github-actions bot removed the stale This issue will be closed soon because of prolonged inactivity label Apr 20, 2025
@github-actions
Copy link
Copy Markdown

Hey! This pull request has been open for quite some time without any new comments now. It will be closed automatically in a week if no further activity occurs.
Thank you for contributing to WLED! ❤️

@github-actions github-actions bot added the stale This issue will be closed soon because of prolonged inactivity label Aug 18, 2025
@github-actions github-actions bot closed this Aug 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

discussion stale This issue will be closed soon because of prolonged inactivity usermod usermod related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants