Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
eaecca9
docs(dali_gear): update readme to match implemented behaviour
netmindz May 23, 2026
0fc601c
esp32s3dev_8MB_opi_dali_gear
netmindz May 23, 2026
23a9360
esp32s3dev_8MB_opi_dali_gear
netmindz May 23, 2026
8394038
fix(dali_gear): address code review comments
netmindz May 24, 2026
6bc19e9
feat(dali_gear): address PR review — TX invert, query responses, QUER…
netmindz May 24, 2026
0d77a27
fix(dali_gear): do not respond to 0xE7 — QUERY COLOUR TYPE is 0xF7 only
netmindz May 24, 2026
670a6fd
feat(dali_gear): respond to QUERY DEVICE TYPE (0x18) with 0x08
netmindz May 24, 2026
71767b7
ci: discover and build custom PlatformIO envs from usermods platformi…
netmindz May 24, 2026
badfcfc
ci: consolidate usermod build envs into per-usermod platformio_overri…
netmindz May 24, 2026
2daf17a
run on push
netmindz May 24, 2026
af4b39e
Fix AHT10_v2 example
netmindz May 24, 2026
d459e6c
no d1_mini env
netmindz May 24, 2026
e68bd2a
no d1_mini env
netmindz May 24, 2026
50cd900
ci: filter usermod matrix to only build changed usermod directories
netmindz May 24, 2026
fb0c1df
SN_Photoresistor
netmindz May 24, 2026
9b4eafa
fix(ci): use PR base SHA for diff and guard jobs against push events
netmindz May 24, 2026
3007564
ESP32 builds all V4
netmindz May 24, 2026
83e32ea
fix: rename/fix platformio_override sample files across usermods
netmindz May 24, 2026
818c3cb
perf: filter custom build matrix to changed usermods on PRs
netmindz May 24, 2026
76a505f
fix release name
netmindz May 24, 2026
76b1e0a
fix release name
netmindz May 24, 2026
133aecb
Merge branch 'usermod-ci' into dali2
netmindz May 24, 2026
fb7e309
Revert "esp32s3dev_8MB_opi_dali_gear"
netmindz May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 104 additions & 10 deletions .github/workflows/usermods.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
pull_request:
paths:
- usermods/**
push:
paths:
- usermods/**

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
Expand All @@ -12,28 +15,40 @@ jobs:

get_usermod_envs:
# Only run for pull requests from forks (not from branches within wled/WLED)
if: github.event.pull_request.head.repo.full_name != github.repository
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
name: Gather Usermods
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
fetch-depth: 0
- name: Get changed usermod environments
id: envs
run: |
echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT
# Usermods whose directories changed in this PR
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} HEAD \
| grep '^usermods/' | cut -d/ -f2 | sort -u || true)

# All usermods with a library.json (excluding known-incompatible ones)
all=$(find usermods/ -name library.json \
| xargs dirname | xargs -n 1 basename \
| grep -v PWM_fan | grep -v BME68X_v2 | grep -v pixels_dice_tray \
| sort || true)

if [ -z "$changed" ] || [ -z "$all" ]; then
echo "usermods=[]" >> $GITHUB_OUTPUT
else
usermods=$(comm -12 <(echo "$all") <(echo "$changed") | jq -R | jq --slurp -c)
echo "usermods=$usermods" >> $GITHUB_OUTPUT
fi
outputs:
usermods: ${{ steps.envs.outputs.usermods }}


build:
# Only run for pull requests from forks (not from branches within wled/WLED)
if: github.event.pull_request.head.repo.full_name != github.repository
# Skip when no changed usermods were found (e.g. only non-library changes)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && needs.get_usermod_envs.outputs.usermods != '[]'
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_usermod_envs
Expand Down Expand Up @@ -74,4 +89,83 @@ jobs:
cat platformio_override.ini

- name: Build firmware
run: pio run -e ${{ matrix.environment }}
run: pio run -e ${{ matrix.environment }}


get_custom_build_envs:
name: Gather Custom Build Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find usermods with custom build environments
id: custom_envs
run: |
# On PRs: only scan usermods whose directories changed.
# On push: scan all usermods (validates the full set on merge).
if [ "${{ github.event_name }}" = "pull_request" ]; then
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} HEAD \
| grep '^usermods/' | cut -d/ -f2 | sort -u || true)
if [ -z "$changed" ]; then
echo "matrix=[]" >> $GITHUB_OUTPUT
exit 0
fi
samples=$(for mod in $changed; do
f="usermods/$mod/platformio_override.ini.sample"
[ -f "$f" ] && echo "$f"
done | sort)
else
samples=$(find usermods/ -name "platformio_override.ini.sample" | sort)
fi

result='[]'
for sample in $samples; do
usermod=$(dirname "$sample" | xargs basename)
envs=$(grep -E '^\[env:[^]]+\]' "$sample" | sed 's/^\[env:\(.*\)\]$/\1/')
for env in $envs; do
result=$(echo "$result" | jq --arg u "$usermod" --arg e "$env" '. + [{usermod: $u, env: $e}]')
done
done
echo "matrix=$(echo "$result" | jq -c '.')" >> $GITHUB_OUTPUT
outputs:
matrix: ${{ steps.custom_envs.outputs.matrix }}


build_custom:
name: Build Custom Env (${{ matrix.usermod }} / ${{ matrix.env }})
runs-on: ubuntu-latest
needs: get_custom_build_envs
if: needs.get_custom_build_envs.outputs.matrix != '[]'
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.get_custom_build_envs.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.env }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.env }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Apply custom build environment
run: cp -v "usermods/${{ matrix.usermod }}/platformio_override.ini.sample" platformio_override.ini
- name: Build firmware
run: pio run -e ${{ matrix.env }}
11 changes: 11 additions & 0 deletions platformio_override.ini
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Move to platformio_override.ini.sample

@coderabbitai why didn't you flag this file?

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if platformio_override.ini is gitignored
cat .gitignore | grep -i platformio_override || echo "not found in .gitignore"

# Check if a .sample file already exists
fd -e sample | grep -i platformio || echo "no .sample platformio file found"

# Check the existing sample file if any
fd "platformio_override.ini.sample" --exec cat {}

Length of output: 4594


@softhack007 — you're absolutely right, and I apologize for missing this. I should have flagged it.

Two clear problems with this file:

  1. platformio_override.ini is in .gitignore — so it shouldn't be committed to the repository at all. Doing so would clobber a developer's local override file when they pull this branch.

  2. Wrong location and naming — the established WLED convention (as seen with usermods/pixels_dice_tray/platformio_override.ini.sample) is to place the example config inside the usermod's own subdirectory with a .sample suffix, i.e.:

    usermods/dali_gear/platformio_override.ini.sample
    

    This keeps it discoverable, version-controlled, and out of the way of any developer's actual local platformio_override.ini.


✏️ Learnings added
Learnt from: softhack007
Repo: wled/WLED PR: 5645
File: platformio_override.ini:1-1
Timestamp: 2026-05-24T08:51:42.355Z
Learning: In the WLED repository, `platformio_override.ini` is listed in `.gitignore` and must never be committed. The correct convention for usermod-specific PlatformIO build environment examples is to place them as `usermods/<modname>/platformio_override.ini.sample` (e.g., `usermods/pixels_dice_tray/platformio_override.ini.sample`).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We need a proper solution for how you build PRs

The sample file isn't included in the CI builds

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You're right, it's a recurring topic and we don't have a good solution. Even when you use the VSCode UI, we'd still need a second file for custom buildenvs that should be included in the CI builds for the PR.

@coderabbitai please open an issue ticket to keep track of this point.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo wled/WLED \
  --title "CI: No proper solution for including custom PlatformIO build envs from PRs in CI builds" \
  --body "## Problem

When a PR introduces a new usermod (or other feature) that requires a custom PlatformIO build environment, there is currently no clean way to include that environment in CI builds for the PR.

- \`platformio_override.ini\` is listed in \`.gitignore\` and **must not be committed** to the repository, so it cannot be used to ship a custom build env in a PR.
- The established workaround is to place an example as \`usermods/<modname>/platformio_override.ini.sample\`, but \`.sample\` files are not picked up by PlatformIO or the CI pipeline.
- Even the [VSCode UI workflow](https://github.com/wled/WLED-Docs/issues/161) does not solve the CI-build side of the problem — a second file would still be needed.

## Impact

- New usermods/features that need non-default build environments cannot be fully validated by CI during code review.
- PR authors resort to committing \`platformio_override.ini\` directly (as seen in PR `#5645`), which clobbers a developer's local override when they pull the branch.

## Possible directions

- A dedicated, version-controlled file (e.g., \`usermods/<modname>/platformio_build_envs.ini\`) that CI explicitly includes/merges.
- A CI mechanism that discovers and appends \`*.ini.sample\` files from changed usermods.
- Documentation of an agreed-upon interim workaround.

## References

- Raised during review of PR `#5645`: https://github.com/wled/WLED/pull/5645#discussion_r3294152922
- Related docs issue: https://github.com/wled/WLED-Docs/issues/161

/cc `@softhack007`" \
  --assignee softhack007

Length of output: 41


@softhack007 — Done! I've opened issue #5648 to track this: #5648

It captures the core problem (no clean way to get custom PlatformIO build envs into CI for PRs), the impact, a few possible directions to explore, and backlinks to this PR and the related docs issue.

🐇📋

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[env:esp32s3dev_8MB_opi_dali_gear]
;; ESP32-S3 dev board (8MB Flash, QSPI PSRAM) with dali_gear usermod
extends = env:esp32s3dev_8MB_qspi
custom_usermods = dali_gear
lib_deps = ${env:esp32s3dev_8MB_qspi.lib_deps}
https://github.com/netmindz/DALI-Lighting-Interface.git#fix/esp32-volatile-cast
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_dali_gear\"
-D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; use UART0 via built-in JTAG/serial debug unit (303a:1001)
-DBOARD_HAS_PSRAM
-D WLED_DEBUG
31 changes: 2 additions & 29 deletions platformio_override.sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -506,42 +506,15 @@ lib_deps = ${esp8266.lib_deps}

# ------------------------------------------------------------------------------
# EleksTube-IPS
# See usermods/EleksTube_IPS/platformio_override.ini.sample
# ------------------------------------------------------------------------------
[env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev
upload_speed = 921600
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D DATA_PINS=12
-D RLYPIN=27
-D BTNPIN=34
-D PIXEL_COUNTS=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder


# ------------------------------------------------------------------------------
# Usermod examples
# ------------------------------------------------------------------------------

# 433MHz RF remote example for esp32dev
[env:esp32dev_usermod_RF433]
extends = env:esp32dev
custom_usermods =
${env:esp32dev.custom_usermods}
RF433
# 433MHz RF remote example: see usermods/usermod_v2_RF433/platformio_override.ini.sample

# External usermod from a git repository.
# The library's `library.json` must include `"build": {"libArchive": false}`.
Expand Down
5 changes: 0 additions & 5 deletions usermods/AHT10_v2/platformio_override.ini

This file was deleted.

9 changes: 9 additions & 0 deletions usermods/AHT10_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# AHT10/AHT15/AHT20 temperature/humidity usermod build environment
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = aht10_example

[env:aht10_example]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} AHT10_v2
9 changes: 9 additions & 0 deletions usermods/BME280_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# BME280_v2 usermod build environment
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = usermod_bme280_esp8266_2m

[env:usermod_esp8266_2m]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} BME280_v2
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
; USERMOD_DHT_MQTT - publish measurements to the MQTT broker
; USERMOD_DHT_STATS - For debug, report delay stats

[env:d1_mini_usermod_dht_C]
extends = env:d1_mini
custom_usermods = ${env:d1_mini.custom_usermods} DHT
build_flags = ${env:d1_mini.build_flags} -D USERMOD_DHT_CELSIUS
[env:esp8266_2m_usermod_dht_C]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} DHT
build_flags = ${env:esp8266_2m.build_flags} -D USERMOD_DHT_CELSIUS

[env:custom32_LEDPIN_16_usermod_dht_C]
extends = env:custom32_LEDPIN_16
Expand Down
32 changes: 32 additions & 0 deletions usermods/EleksTube_IPS/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# EleksTube IPS clock build environment
# Copy to platformio_override.ini in the WLED root to use.
#
# Note: usermods/EleksTube_IPS/library.json is currently disabled.
# To enable custom_usermods support, rename library.json.disabled to library.json.

[platformio]
default_envs = elekstube_ips

[env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev
upload_speed = 921600
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D DATA_PINS=12
-D RLYPIN=27
-D BTNPIN=34
-D PIXEL_COUNTS=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
6 changes: 0 additions & 6 deletions usermods/INA226_v2/platformio_override.ini

This file was deleted.

23 changes: 23 additions & 0 deletions usermods/INA226_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# INA226 power monitor usermod build environments
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = ina226_example

# Minimal example — enable the usermod with default settings
[env:ina226_example]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
build_flags = ${env:esp32dev.build_flags}
; -D USERMOD_INA226_DEBUG ; uncomment to add debug status to the info modal

# Custom calibration example — adjust constants to match your hardware
[env:ina226_custom]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} INA226_v2
build_flags = ${env:esp32dev.build_flags}
-D INA226_ENABLED_DEFAULT=true
-D INA226_SHUNT_MICRO_OHMS=2888
-D INA226_DEFAULT_CURRENT_RANGE=10000
-D INA226_CURRENT_OFFSET_MA=-118
-D INA226_CHECK_INTERVAL_MS=1000
9 changes: 9 additions & 0 deletions usermods/LD2410_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# LD2410 presence sensor usermod build environment
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = usermod_LD2410_v2_esp32dev

[env:usermod_LD2410_v2_esp32dev]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} LD2410_v2
10 changes: 10 additions & 0 deletions usermods/LDR_Dusk_Dawn_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# LDR Dusk/Dawn usermod build environment
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = usermod_LDR_Dusk_Dawn_esp32dev

[env:usermod_LDR_Dusk_Dawn_esp32dev]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods}
LDR_Dusk_Dawn
9 changes: 9 additions & 0 deletions usermods/MAX17048_v2/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# MAX17048 battery fuel gauge usermod build environment
# Copy to platformio_override.ini in the WLED root to use.

[platformio]
default_envs = usermod_max17048_esp8266_2m

[env:usermod_max17048_esp8266_2m]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} MAX17048_v2
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
; Options
; -------
; USERMOD_SN_PHOTORESISTOR - define this to have this user mod included wled00\usermods_list.cpp
; USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds
; USERMOD_SN_PHOTORESISTOR_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds
; USERMOD_SN_PHOTORESISTOR_REFERENCE_VOLTAGE - the voltage supplied to the sensor, defaults to 5v
; USERMOD_SN_PHOTORESISTOR_ADC_PRECISION - the ADC precision is the number of distinguishable ADC inputs, defaults to 1024.0 (10 bits)
; USERMOD_SN_PHOTORESISTOR_RESISTOR_VALUE - the resistor size, defaults to 10000.0 (10K hms)
; USERMOD_SN_PHOTORESISTOR_OFFSET_VALUE - the offset value to report on, defaults to 25
;
[env:usermod_sn_photoresistor_d1_mini]
extends = env:d1_mini
[env:usermod_sn_photoresistor_esp8266_2m]
extends = env:esp8266_2m
custom_usermods = ${env:esp8266_2m.custom_usermods} SN_Photoresistor
build_flags =
${common.build_flags_esp8266}
-D USERMOD_SN_PHOTORESISTOR
-D USERMOD_SN_PHOTORESISTOR_MEASUREMENT_INTERVAL=60
lib_deps = ${env.lib_deps}
8 changes: 0 additions & 8 deletions usermods/TTGO-T-Display/platformio_override.ini

This file was deleted.

16 changes: 16 additions & 0 deletions usermods/TTGO-T-Display/platformio_override.ini.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
; TTGO-T-Display usermod build example.
; Note: this usermod has no library.json so custom_usermods is not available.
; The usermod.cpp must be included manually in your build.

[platformio]
default_envs = ttgo_t_display_example

[env:ttgo_t_display_example]
extends = env:esp32dev
build_flags = ${env:esp32dev.build_flags}
; PIN defines - uncomment and change, if needed:
; -D LEDPIN=2
-D BTNPIN=35
; -D IRPIN=4
; -D RLYPIN=12
; -D RLYMDE=1
5 changes: 0 additions & 5 deletions usermods/Temperature/platformio_override.ini

This file was deleted.

Loading
Loading