-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Add boot_lor usermod - enforces a configured realtime override at startup #5569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
+337
−0
Closed
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
ce0ca4e
Add boot_lor usermod
smitty078 d58db84
Applied all actionable coderabbitai changes
smitty078 a8c4dce
Removed waitForUDP option as it turns out it does not work with DDP t…
smitty078 e481012
Yet more coderabbit suggestions
smitty078 cd65893
Clarifies behavior in readme. Maybe the last coderabbit?
smitty078 9d3e2da
Clarify network activity wait condition in readme
smitty078 f6759a6
Update note on realtime streaming behavior
smitty078 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "name": "boot_lor", | ||
| "version": "1.0.0", | ||
| "description": "Set WLED realtime override at boot", | ||
| "frameworks": "arduino", | ||
| "platforms": "espressif32", | ||
| "build": { | ||
| "libArchive": false, | ||
| "srcDir": "." | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| # Usermods API v2 boot_lor usermod | ||
|
|
||
| ## Installation | ||
|
|
||
| Add `boot_lor` to `custom_usermods` in your PlatformIO environment and compile! | ||
|
|
||
| ## Overview | ||
|
|
||
| `boot_lor` is a usermod that sets the WLED realtime override mode (`lor`) at boot. | ||
|
|
||
| It is designed for setups where WLED is primarily controlled via external APIs (e.g. HomeKit, Home Assistant, or custom integrations), and realtime streaming protocols (such as DDP) are only used occasionally. | ||
|
|
||
| By default, WLED enables realtime streaming at boot when data is received. This can interfere with API-driven control flows. This usermod ensures that a desired `lor` mode (typically `lor:2`) is enforced during startup. | ||
|
|
||
| --- | ||
|
|
||
| ## Use Case | ||
|
|
||
| This usermod is intended for environments where: | ||
|
|
||
| - WLED is primarily controlled via an external system (e.g. HomeKit via Homebridge, Home Assistant, or direct API usage) | ||
| - Multiple controllers should behave as **independent devices** under normal operation | ||
| - Realtime streaming (DDP, E1.31, etc.) is used **only when explicitly enabled** | ||
|
|
||
| ### Example scenario | ||
|
|
||
| - Two WLED controllers are used as separate lights in HomeKit | ||
| - A secondary setup uses WLED "virtual LEDs" to mirror one controller to another via DDP | ||
| - This works well for effects and synchronized control | ||
|
|
||
| **Problem:** | ||
| - At boot, realtime communication may take control as soon as packets arrive, overriding API-based control unexpectedly. | ||
| - Using JSON or HTTP API in a boot preset does not reliably set lor | ||
|
|
||
| **Solution:** | ||
| Set `lor:2` at boot to disable realtime takeover by default. | ||
| When realtime control is desired, manually switch back to `lor:0`. | ||
|
|
||
| --- | ||
|
|
||
| ## Behavior | ||
|
|
||
| The usermod applies the configured `lor` value during startup using the following sequence: | ||
|
|
||
| ### Default behavior | ||
|
|
||
| ```text | ||
| WiFi connected → (optional delay) → apply lor → assert for N seconds → stop | ||
| ``` | ||
|
|
||
| - Waits for network connectivity | ||
| - Optionally waits for the first UDP packet (recommended for DDP setups) | ||
| - Waits an additional configurable delay (if set) | ||
| - Applies the configured `lor` value | ||
| - Reasserts it for a short period to allow system "settling" | ||
| - Stops running after completion | ||
|
|
||
| --- | ||
|
|
||
| ## Configuration | ||
|
|
||
| The following options are available under `"boot_lor"` in the WLED config: | ||
|
|
||
| ```json | ||
| "boot_lor": { | ||
| "bootLor": 2, | ||
| "waitForUdpPacket": true, | ||
| "additionalWaitSec": 0, | ||
| "assertForSec": 10 | ||
| } | ||
| ``` | ||
|
|
||
| ### Options | ||
|
|
||
| | Name | Type | Default | Description | | ||
| |---------------------|------|---------|-------------| | ||
| | `bootLor` | int | `2` | Realtime override mode to apply. Valid values: `-1` (disabled), `0`, `1`, `2` | | ||
| | `waitForUdpPacket` | bool | `true` | Wait for first UDP packet before starting the delay timer | | ||
| | `additionalWaitSec` | int | `0` | Additional delay (in seconds) after trigger (connection or UDP) before applying | | ||
| | `assertForSec` | int | `10` | Duration (in seconds) to reassert the value after first application | | ||
|
|
||
| --- | ||
|
|
||
| ## Recommended Settings | ||
|
|
||
| For most DDP / API-first setups: | ||
|
|
||
| ```json | ||
| { | ||
| "bootLor": 2, | ||
| "waitForUdpPacket": true, | ||
| "additionalWaitSec": 0, | ||
| "assertForSec": 10 | ||
| } | ||
| ``` | ||
|
|
||
| This ensures: | ||
|
|
||
| - Realtime streaming does not take control unexpectedly at boot | ||
| - The system waits for actual network activity before acting | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| - The setting is reinforced briefly to avoid race conditions | ||
|
|
||
| --- | ||
|
|
||
| ## Notes | ||
|
|
||
| - This usermod does **not** block or modify UDP traffic | ||
| - It does **not** interfere with realtime streaming once `lor` is manually changed | ||
| - It simply ensures a predictable startup state | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| --- | ||
|
|
||
| ## Status | ||
|
|
||
| After completion, the usermod stops running and has no ongoing impact on system performance. | ||
|
|
||
| --- | ||
|
|
||
| ## JSON Info | ||
|
|
||
| The current state is exposed under `info -> u`: | ||
|
|
||
| ```text | ||
| Boot LOR: [bootLor, state, realtimeOverride] | ||
| ``` | ||
|
|
||
| Where: | ||
| - `state` is one of `waiting`, `applied`, or `finished` | ||
|
|
||
| --- | ||
|
|
||
| ## Tested | ||
| - Build: esp32dev | ||
| - Runtime: tested on ESP32-D0WD-V3 | ||
|
|
||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| This usermod provides a simple and reliable way to: | ||
|
|
||
| - Default to API-based control at boot | ||
| - Avoid unintended realtime takeover | ||
| - Retain full flexibility to enable realtime modes when desired | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| #include "wled.h" | ||
|
|
||
| /** | ||
| * @brief Applies a configured WLED realtime override mode during startup. | ||
| * | ||
| * The usermod waits for network connectivity, waits an optional additional | ||
| * delay, then applies the configured realtime override value and reasserts it | ||
| * for a short settling period. | ||
| */ | ||
| class BootLorUsermod : public Usermod { | ||
| private: | ||
| static constexpr const char* _name = "boot_lor"; ///< JSON configuration key for this usermod. | ||
|
|
||
| int8_t bootLor = 2; ///< Realtime override mode to apply; -1 disables the usermod. | ||
| uint8_t additionalWaitSec = 0; ///< Additional delay, in seconds, after network connection. | ||
| uint16_t assertForSec = 10; ///< Duration, in seconds, to reassert the configured override. | ||
|
|
||
| unsigned long connectedMs = 0; ///< Timestamp when network connectivity became available. | ||
| unsigned long firstAppliedMs = 0; ///< Timestamp of the first successful realtime override application. | ||
|
|
||
| bool connectedSeen = false; ///< True once the network connection timestamp has been captured. | ||
| bool applied = false; ///< True once the configured realtime override has been applied. | ||
| bool finished = false; ///< True once the assertion window has completed. | ||
|
|
||
| /** | ||
| * @brief Checks whether a realtime override value is valid. | ||
| * | ||
| * @param value Realtime override value to validate. | ||
| * @return true if the value is -1, 0, 1, or 2. | ||
| */ | ||
| bool isValidLor(int8_t value) const { | ||
| return value >= -1 && value <= 2; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Checks whether this usermod should run. | ||
| * | ||
| * @return true when the configured realtime override is valid and not disabled. | ||
| */ | ||
| bool isEnabled() const { | ||
| return isValidLor(bootLor) && bootLor >= 0; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Stores the timestamp used to start the post-connection wait period. | ||
| * | ||
| * @param now Current millis() timestamp. | ||
| */ | ||
| void setConnectedTime(unsigned long now) { | ||
| connectedMs = now; | ||
| connectedSeen = true; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Checks whether the configured post-connection wait period has elapsed. | ||
| * | ||
| * @return true once enough time has passed since network connection. | ||
| */ | ||
| bool additionalWaitElapsed() const { | ||
| const unsigned long waitMs = (unsigned long)additionalWaitSec * 1000UL; | ||
| return millis() - connectedMs >= waitMs; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Checks whether all conditions are met to apply the realtime override. | ||
| * | ||
| * @return true when the usermod is enabled, connected, and past its wait period. | ||
| */ | ||
| bool readyToApply() const { | ||
| if (!isEnabled() || finished || !connectedSeen) return false; | ||
| if (!WLED_CONNECTED) return false; | ||
|
|
||
| return additionalWaitElapsed(); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Applies the configured realtime override and records first application time. | ||
| */ | ||
| void applyBootLor() { | ||
| if (realtimeOverride != bootLor) { | ||
| realtimeOverride = bootLor; | ||
| } | ||
|
|
||
| if (!applied) { | ||
| applied = true; | ||
| firstAppliedMs = millis(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @brief Applies and reasserts the configured realtime override when ready. | ||
| */ | ||
| void runIfReady() { | ||
| if (!readyToApply()) return; | ||
|
|
||
| applyBootLor(); | ||
|
|
||
| if (millis() - firstAppliedMs > (unsigned long)assertForSec * 1000UL) { | ||
| finished = true; | ||
| } | ||
| } | ||
|
|
||
| public: | ||
| /** | ||
| * @brief Initializes the usermod. | ||
| */ | ||
| void setup() override { | ||
| } | ||
|
|
||
| /** | ||
| * @brief Starts the wait timer when networking becomes available. | ||
| */ | ||
| void connected() override { | ||
| if (!connectedSeen) { | ||
| setConnectedTime(millis()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @brief Runs the non-blocking assertion state machine. | ||
| */ | ||
| void loop() override { | ||
| runIfReady(); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Adds this usermod's settings to the WLED configuration JSON. | ||
| * | ||
| * @param root Root configuration JSON object. | ||
| */ | ||
| void addToConfig(JsonObject& root) override { | ||
| JsonObject top = root[_name]; | ||
| if (top.isNull()) top = root.createNestedObject(_name); | ||
|
|
||
| top["bootLor"] = bootLor; | ||
| top["additionalWaitSec"] = additionalWaitSec; | ||
| top["assertForSec"] = assertForSec; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Reads this usermod's settings from the WLED configuration JSON. | ||
| * | ||
| * @param root Root configuration JSON object. | ||
| * @return true if this usermod's configuration object exists. | ||
| */ | ||
| bool readFromConfig(JsonObject& root) override { | ||
| JsonObject top = root[_name]; | ||
| if (top.isNull()) return false; | ||
|
|
||
| int8_t newBootLor = top["bootLor"] | bootLor; | ||
| if (isValidLor(newBootLor)) bootLor = newBootLor; | ||
|
|
||
| additionalWaitSec = top["additionalWaitSec"] | additionalWaitSec; | ||
| assertForSec = top["assertForSec"] | assertForSec; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Adds runtime status information to the WLED info JSON. | ||
| * | ||
| * @param root Root info JSON object. | ||
| */ | ||
| void addToJsonInfo(JsonObject& root) override { | ||
| JsonObject user = root["u"]; | ||
| if (user.isNull()) user = root.createNestedObject("u"); | ||
|
|
||
| JsonArray infoArr = user.createNestedArray("Boot LOR"); | ||
| infoArr.add(bootLor); | ||
| infoArr.add(finished ? "finished" : applied ? "applied" : "waiting"); | ||
| infoArr.add(realtimeOverride); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Returns the registered usermod identifier. | ||
| * | ||
| * @return USERMOD_ID_BOOT_LOR. | ||
| */ | ||
| uint16_t getId() override { | ||
| return USERMOD_ID_BOOT_LOR; | ||
| } | ||
| }; | ||
|
|
||
| static BootLorUsermod boot_lor_usermod; | ||
| REGISTER_USERMOD(boot_lor_usermod); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.