NeoPi
NeoPi is a Python package for running individually addressable RGB and RGBW lightstrips from a Raspberry Pi. It includes a local web dashboard, a library of general and seasonal animations, editable presets, automatic animation queues, a hardware-free simulator, and optional Philips Hue room-color synchronization.
NeoPi is designed for two kinds of use:
- Install the package on a Raspberry Pi and control a physical strip from a browser.
- Clone the repository, run it directly in mock or hardware mode, and create animations in ordinary Python modules.
Python 3.9 or newer is required.
Install and run on a Raspberry Pi
Create a virtual environment and install the published package with its hardware drivers:
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install "neopi[hardware]"
Keep NeoPi's settings in your normal user's home directory, including when the server needs elevated GPIO access:
export NEOPI_DATA_DIR="$HOME/.config/neopi"
.venv/bin/neopi setup
sudo --preserve-env=NEOPI_DATA_DIR .venv/bin/neopi serve
Open http://<raspberry-pi-hostname-or-ip>:8000 from a device on the same network. If your account already has permission to access the GPIO hardware, run .venv/bin/neopi serve without sudo.
neopi setup asks for the pixel count, channel order, and GPIO data pin. The generated hardware configuration is stored at ~/.config/neopi/config.json.
Hardware power
Use a separate power supply that matches the strip's rated voltage and can supply its maximum current. Connect the Raspberry Pi ground to the strip power-supply ground, but do not power a long strip from a Pi power pin. A level shifter, a 300–500 ohm data resistor, and a 500–1000 µF capacitor near the strip input are recommended. Verify voltage, ground, data direction, and channel order before starting NeoPi.
Run directly from a cloned repository
The repository includes run_source.py, which adds src/ to Python's import path and launches the same CLI as the installed package. This lets you run NeoPi without installing NeoPi itself. Its third-party dependencies still need to be installed in a virtual environment.
Clone the repository and enter it before following one of the paths below:
git clone https://github.com/tvarovski/neopi.git
cd neopi
Mock mode on Linux or macOS
python3 -m venv .venv
.venv/bin/python -m pip install "fastapi>=0.110,<1" "pydantic>=2,<3" "uvicorn[standard]>=0.29,<1" "pytest>=8" "httpx>=0.27"
NEOPI_DRIVER=simulator .venv/bin/python run_source.py serve --reload
Mock mode on Windows PowerShell
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install "fastapi>=0.110,<1" "pydantic>=2,<3" "uvicorn[standard]>=0.29,<1" "pytest>=8" "httpx>=0.27"
$env:NEOPI_DRIVER = "simulator"
.\.venv\Scripts\python.exe run_source.py serve --reload
Open http://127.0.0.1:8000. Mock mode executes the real server and animation code while storing frames in memory instead of sending them to LEDs. The dashboard's live preview shows a sample of the generated strip output.
Set NEOPI_DRIVER=simulator explicitly when testing. On a non-Raspberry Pi host, driver: auto also falls back to the simulator, but the explicit setting makes the intended mode clear and prevents accidental hardware initialization.
Hardware mode from a Raspberry Pi checkout
Install the runtime and hardware dependencies without installing NeoPi:
python3 -m venv .venv
.venv/bin/python -m pip install "fastapi>=0.110,<1" "pydantic>=2,<3" "uvicorn[standard]>=0.29,<1" "Adafruit-Blinka>=8" "adafruit-circuitpython-neopixel>=6" "rpi-ws281x>=5"
export NEOPI_DATA_DIR="$HOME/.config/neopi"
.venv/bin/python run_source.py setup
sudo --preserve-env=NEOPI_DATA_DIR .venv/bin/python run_source.py serve
Changes under src/neopi/ take effect after a restart. During development, add --reload to restart the web process automatically when Python files change.
Editable development installation
If you are comfortable installing the checkout as a package, an editable install is shorter and keeps code changes immediately available:
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
NEOPI_DRIVER=simulator .venv/bin/neopi serve --reload
On a Raspberry Pi, use .[hardware,dev] and follow the same configuration and GPIO guidance as the installed-package path above.
Configure the strip
The default configuration file is ~/.config/neopi/config.json, and all fields are shown in config.example.json. For example, a two-meter RGBW strip with 144 pixels per meter could use:
{
"num_pixels": 288,
"pixel_order": "GRBW",
"pixel_pin": "D21",
"startup_brightness": 0.2,
"rgbw_white_point_kelvin": 4000,
"rgbw_white_luminance_scale": 1.0,
"rgb_calibration": {
"red": {"x": 0.6911, "y": 0.3045, "luminance": 310.0},
"green": {"x": 0.1407, "y": 0.7034, "luminance": 800.0},
"blue": {"x": 0.1456, "y": 0.0872, "luminance": 190.0}
},
"driver": "auto"
}
num_pixelsis the total number of individually addressable pixels.pixel_orderacceptsRGB,GRB,RGBW, orGRBW.pixel_pinaccepts the supported board namesD10,D12,D18, orD21.startup_brightnessis a global multiplier from 0.0 through 1.0.rgbw_white_point_kelvindescribes the physical white LED and accepts 1500–10000 K.rgbw_white_luminance_scaledescribes W-diode output relative to modeled RGB white and accepts 0.1–10.0. Raise it when white output is too strong; a value of 2.0 sends half as much W for the same modeled contribution.rgb_calibrationdefines each physical RGB primary as CIE 1931x/ycoordinates plus relative luminance. Its defaults use measured WS2815 RGB chromaticities and the WS2815B chip's typical 310/800/190 mcd R/G/B intensity values. Change these only when you have measurements for another strip.driveracceptsauto,neopixel, orsimulator. TheNEOPI_DRIVERenvironment variable overrides it.
Restart NeoPi after changing hardware configuration. Controller state and Hue credentials are stored separately in the same data directory.
Use the dashboard
The dashboard lets you select an animation, change its exposed parameters, and then play it immediately or add it to the automatic queue. Changes to the active manual animation are sent to the running animation. Queue order, cycle duration, transition duration, and other controller settings persist across restarts.
RGBW color parameters include a separate white-channel control. NeoPi represents RGB colors as #RRGGBB and RGBW colors as #RRGGBBWW when exchanging values with the dashboard.
Create or modify an animation
Each built-in animation lives in its own module under src/neopi/animations/. To add one to the dashboard:
- Create the animation module.
- Import the function and add it to
ANIMATIONSinsrc/neopi/animations/__init__.py. - Add one or more entries directly to
PRESETS_DEFINITIONSinsrc/neopi/presets.py. - Run the tests and start the source checkout in mock mode.
The animation function must accept wait and duration. Other defaulted numeric and RGB/RGBW tuple parameters are inspected to create dashboard controls automatically.
"""A small example of a custom NeoPi animation."""
import time
from neopi.config import NUM_PIXELS, pixels
from neopi.type_defs import RGBWColor
from neopi.utils import fade_transition
@fade_transition()
def my_custom_pattern(
wait: float = 0.05,
duration: float = 10,
main_color: RGBWColor = (255, 0, 128, 0),
) -> None:
"""Move a configurable color pattern along the strip.
Args:
wait: Delay between frames in seconds.
duration: Total animation runtime in seconds.
main_color: Red, green, blue, and white channel values.
"""
started = time.monotonic()
frame = 0
while time.monotonic() - started < duration:
for index in range(frame % 2, NUM_PIXELS, 2):
pixels[index] = main_color if (index + frame) % 12 < 6 else (0, 0, 0, 0)
pixels.show()
time.sleep(max(0.01, wait))
frame += 1
Register the function:
from .my_custom_pattern import my_custom_pattern
ANIMATIONS = {
# Existing entries...
"my_custom_pattern": my_custom_pattern,
}
Then add its dashboard preset directly inside PRESETS_DEFINITIONS:
{
"id": "my_custom_pattern",
"func_name": "my_custom_pattern",
"label": "My Custom Pattern",
"description": "Alternating neon-colored bands moving along the strip.",
"category": "General",
"defaults": {
"wait": 0.05,
"main_color": "#ff008000"
}
},
Use Google-style docstrings and type hints. Expose meaningful colors, speeds, sizes, densities, and probabilities as defaulted function parameters instead of embedding them in the rendering loop. duration is managed by the server and is not rendered as a dashboard control.
For long, dense strips, animations use interlaced updates to reduce sudden current changes and data instability:
for index in range(frame % 2, NUM_PIXELS, 2):
pixels[index] = color
Avoid calling pixels.fill() on every frame. Use it for initialization or shutdown, and update moving content through the interlaced loop.
Run the complete validation suite from the repository root:
.venv/bin/python -m pytest
On Windows, use .\.venv\Scripts\python.exe -m pytest.
Run without the web server
example_serverless.py runs a small loop containing Rainbow Cycle, Color Waves, and Counterflow Waves:
python example_serverless.py
It works directly from a source checkout because it adds src/ to its import path. It uses the same hardware configuration and driver as the server, but does not start FastAPI, save a queue, or synchronize with Philips Hue. Set NEOPI_DRIVER=simulator to run it without LEDs. Press Ctrl+C to stop and blank the strip.
Philips Hue room synchronization
Start NeoPi, open Hue Room Sync, enter the Hue Bridge LAN address, and press the physical bridge link button before pairing. Load the rooms, select a room or zone, choose a palette mode, set the refresh interval, and enable synchronization. Hue polling defaults to 10 seconds and accepts 5–3600 seconds; animation speed remains independent.
The seven Hue-aware animations continuously blend toward the latest shared room palette. NeoPi reads one bridge snapshot per refresh interval, filters for powered-on and reachable bulbs, and uses their current XY, hue/saturation, or color-temperature state. The last successful palette is shared by every Hue animation and saved locally, so changing animations or restarting NeoPi does not restore an animation-specific palette. Before the first successful bridge reading, Hue animations leave the strip unchanged rather than generating fallback colors.
- Inferred scene colors groups similar bulbs and keeps up to five observed representative colors, including isolated accents.
- Direct bulb colors keeps every exact unique bulb color in bridge room order.
NeoPi samples current bulb states, including dynamic scenes, rather than reading the stored definition of a named scene. It never changes the Hue bulbs. If polling fails or no bulbs are available, the last usable palette remains active.
Hue XY colors are solved against the strip's configured physical RGB-to-XYZ matrix. The default profile combines the WS2815B chip's typical per-channel intensity with measured primary chromaticities from a SuperLightingLED WS2815 RGBW spectrum report. This compensates for the green diode producing substantially more light than red or blue at the same numeric drive. Hue XY and hue/saturation colors use RGB only so the white diode cannot wash out scene colors. Hue color-temperature states use rgbw_white_point_kelvin to model the W emitter and rgbw_white_luminance_scale to compensate for its relative output, retaining any warm or cool correction in RGB.
The integration uses the Hue Bridge local v1 REST API and the Hue link-button pairing workflow. It requires bridge-connected bulbs; direct Bluetooth and Zigbee pairing are outside NeoPi. Credentials remain in the local NeoPi data directory. Because the local v1 API uses HTTP, use Hue synchronization only on a trusted LAN.
Files and architecture
NeoPi uses a src package layout:
neopi/
├── pyproject.toml
├── README.md
├── run_source.py # Full CLI from a source checkout
├── example_serverless.py # Small loop without FastAPI
├── src/neopi/
│ ├── app.py # FastAPI application and controller
│ ├── cli.py # setup and serve commands
│ ├── config.py # Hardware initialization and simulator
│ ├── hue.py # Hue polling and color conversion
│ ├── presets.py # Dashboard preset catalog
│ ├── animations/ # One module per animation
│ └── static/ # Dashboard HTML, CSS, and JavaScript
└── tests/
Configuration defaults to these paths:
~/.config/neopi/config.json: strip hardware configuration.~/.config/neopi/settings.json: dashboard and queue state.~/.config/neopi/hue.json: Hue settings and bridge credential.
Set NEOPI_DATA_DIR to move all three files. NEOPI_CONFIG_FILE, NEOPI_SETTINGS_FILE, and NEOPI_HUE_SETTINGS_FILE override individual paths. Older checkout-local lightstrip_config.json, settings.json, and hue_settings.json files are not migrated automatically.
Run as a systemd service
Create /etc/systemd/system/neopi.service after replacing the user and virtual-environment paths. The service account must have access to the GPIO driver. If hardware access requires root, use User=root while keeping NEOPI_DATA_DIR pointed at the intended persistent configuration directory.
[Unit]
Description=NeoPi lightstrip server
After=network.target
[Service]
User=yourusername
Environment=NEOPI_DATA_DIR=/home/yourusername/.config/neopi
ExecStart=/home/yourusername/neopi-venv/bin/neopi serve
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and inspect it with:
sudo systemctl daemon-reload
sudo systemctl enable --now neopi.service
sudo systemctl status neopi.service
sudo journalctl -u neopi.service -f
Restart the service after installing a new NeoPi release:
sudo systemctl restart neopi.service
Build a package
From a development installation containing a PEP 517 build frontend:
python -m build
The wheel includes the Python package, built-in animations, type marker, and dashboard assets. On Windows, publish.cmd builds distributions and invokes Twine for publishing.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neopi-0.1.11.tar.gz.
File metadata
- Download URL: neopi-0.1.11.tar.gz
- Upload date:
- Size: 162.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bf4f57e0df32bd1c21a2f00ccd70a364a50236d6ee0be3eb1d38edfca0ebe03
|
|
| MD5 |
58e6292efd88a8bc2ece4426eac950e5
|
|
| BLAKE2b-256 |
6a509835c7eec6c35b677dc3fff5d1d1d24009c5211543f494a0a5e5fc1e1ac6
|
File details
Details for the file neopi-0.1.11-py3-none-any.whl.
File metadata
- Download URL: neopi-0.1.11-py3-none-any.whl
- Upload date:
- Size: 175.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca6ede9bfadf1dd77eceff2d599b76e7e6d98a79de84476080bc5597cc30dddb
|
|
| MD5 |
83fe6767246a1d23292de347adfaad5f
|
|
| BLAKE2b-256 |
fb5a2ae0da0f1e95653ef82329f2dc76cdc19589e11f851ef31cd7d6dd480127
|