Skip to content

Commit 51ef57e

Browse files
committed
resolve: merge conflict in mkdocs.yml navigation
- Combine Effects page from master with Contributing subsections - Maintain proper navigation hierarchy with both Effects and enhanced Contributing sections - All documentation builds successfully
2 parents 8fedfeb + badb1da commit 51ef57e

File tree

3 files changed

+688
-0
lines changed

3 files changed

+688
-0
lines changed

CONTRIBUTING.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,231 @@ busylight/
168168
- **Async/Await**: Non-blocking effects and concurrent operations
169169
- **Dependency Injection**: Testable components with clear interfaces
170170

171+
## Effects Development
172+
173+
The Effects system is the core of BusyLight's dynamic lighting capabilities.
174+
Understanding how to create and modify effects is essential for contributors.
175+
176+
### Effects Architecture
177+
178+
All effects inherit from `BaseEffect` in `src/busylight/effects/effect.py`:
179+
180+
```python
181+
class BaseEffect(abc.ABC):
182+
@property
183+
@abc.abstractmethod
184+
def colors(self) -> list[tuple[int, int, int]]:
185+
"""List of RGB color tuples defining the effect sequence."""
186+
187+
@property
188+
@abc.abstractmethod
189+
def default_interval(self) -> float:
190+
"""Default interval between color changes in seconds."""
191+
```
192+
193+
### Built-in Effects
194+
195+
- **`Steady`**: Static color display
196+
- **`Blink`**: Alternates between two colors
197+
- **`Spectrum`**: Rainbow color cycling with sine wave generation
198+
- **`Gradient`**: Smooth fade from black to color and back
199+
200+
### Creating New Effects
201+
202+
#### 1. Basic Effect Structure
203+
204+
```python
205+
from typing import TYPE_CHECKING
206+
from busylight_core.mixins.taskable import TaskPriority
207+
from busylight.effects.effect import BaseEffect
208+
209+
if TYPE_CHECKING:
210+
from busylight_core import Light
211+
212+
class MyEffect(BaseEffect):
213+
def __init__(self, color: tuple[int, int, int]) -> None:
214+
self.color = color
215+
self.priority = TaskPriority.NORMAL
216+
217+
@property
218+
def colors(self) -> list[tuple[int, int, int]]:
219+
# Return your color sequence
220+
return [self.color, (0, 0, 0)] # Color + black
221+
222+
@property
223+
def default_interval(self) -> float:
224+
return 0.5 # 500ms between changes
225+
```
226+
227+
#### 2. Color Generation Patterns
228+
229+
**Static Sequences:**
230+
```python
231+
@property
232+
def colors(self) -> list[tuple[int, int, int]]:
233+
return [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
234+
```
235+
236+
**Computed Sequences:**
237+
```python
238+
@property
239+
def colors(self) -> list[tuple[int, int, int]]:
240+
if hasattr(self, "_colors"):
241+
return self._colors
242+
243+
# Generate color sequence
244+
colors = []
245+
for i in range(10):
246+
intensity = int(255 * (i / 10))
247+
colors.append((intensity, 0, 0))
248+
249+
self._colors = colors # Cache result
250+
return self._colors
251+
```
252+
253+
**Mathematical Functions:**
254+
```python
255+
import math
256+
257+
@property
258+
def colors(self) -> list[tuple[int, int, int]]:
259+
colors = []
260+
for i in range(50):
261+
# Sine wave for smooth transitions
262+
r = int(127 * math.sin(i * 0.1) + 128)
263+
g = int(127 * math.cos(i * 0.1) + 128)
264+
colors.append((r, g, 0))
265+
return colors
266+
```
267+
268+
#### 3. Advanced Execution Control
269+
270+
For complex timing or custom logic, override `execute()`:
271+
272+
```python
273+
async def execute(self, light: "Light", interval: float | None = None) -> None:
274+
"""Custom execution with variable timing."""
275+
try:
276+
for i, color in enumerate(self.colors):
277+
light.on(color)
278+
279+
# Variable timing based on position
280+
if i % 2 == 0:
281+
await asyncio.sleep(0.1) # Fast
282+
else:
283+
await asyncio.sleep(0.5) # Slow
284+
285+
finally:
286+
light.off() # Always cleanup
287+
```
288+
289+
### Effect Development Guidelines
290+
291+
#### Performance Best Practices
292+
293+
1. **Cache Color Calculations**: Use `self._colors` to avoid recomputation
294+
2. **Reasonable Sequence Length**: Keep under 1000 colors for performance
295+
3. **Appropriate Timing**: Balance smoothness with device capabilities
296+
4. **Memory Efficiency**: Generate colors lazily when possible
297+
298+
#### Testing Effects
299+
300+
**Unit Tests:**
301+
```python
302+
def test_effect_colors():
303+
effect = MyEffect(color=(255, 0, 0))
304+
colors = effect.colors
305+
306+
assert len(colors) > 0
307+
assert all(len(color) == 3 for color in colors)
308+
assert all(0 <= c <= 255 for color in colors for c in color)
309+
310+
async def test_effect_execution():
311+
effect = MyEffect(color=(255, 0, 0))
312+
mock_light = Mock()
313+
314+
await effect.execute(mock_light)
315+
assert mock_light.on.called
316+
assert mock_light.off.called
317+
```
318+
319+
**Integration Testing:**
320+
```bash
321+
# Test with actual hardware
322+
busylight --debug on red # Test steady effect
323+
busylight --debug blink blue --count 3 # Test blink effect
324+
```
325+
326+
#### Effect Registration
327+
328+
Add new effects to `src/busylight/effects/__init__.py`:
329+
330+
```python
331+
from .my_effect import MyEffect
332+
333+
__all__ = [
334+
"Blink",
335+
"Effects",
336+
"Gradient",
337+
"MyEffect", # Add here
338+
"Spectrum",
339+
"Steady",
340+
]
341+
```
342+
343+
#### CLI Integration
344+
345+
Effects are automatically available via the discovery system:
346+
347+
```python
348+
# In CLI code
349+
effect_class = BaseEffect.for_name("myeffect")
350+
effect = effect_class(color=(255, 0, 0))
351+
```
352+
353+
### Effect Debugging
354+
355+
#### Common Issues
356+
357+
**Colors Not Displaying:**
358+
- Verify RGB values are 0-255
359+
- Check that `colors` property returns non-empty list
360+
- Ensure `default_interval` > 0
361+
362+
**Performance Problems:**
363+
- Profile color generation with large sequences
364+
- Cache expensive computations in `_colors`
365+
- Use appropriate sleep intervals
366+
367+
**Memory Leaks:**
368+
- Clear cached data in `reset()` method if needed
369+
- Avoid circular references in effect objects
370+
371+
#### Debug Utilities
372+
373+
```python
374+
# Add to effect class for debugging
375+
def debug_info(self) -> dict:
376+
return {
377+
"name": self.name,
378+
"color_count": len(self.colors),
379+
"interval": self.default_interval,
380+
"priority": self.priority.name,
381+
}
382+
```
383+
384+
### Effect Examples
385+
386+
See **[complete effects documentation][docs-effects]** for:
387+
- Detailed API reference
388+
- Advanced color generation techniques
389+
- Mathematical effect patterns
390+
- Performance optimization strategies
391+
- Integration examples
392+
393+
Contributing new effects expands BusyLight's creative possibilities and
394+
benefits the entire community!
395+
171396
## Development Workflow
172397

173398
### 1. Issue First
@@ -489,6 +714,7 @@ control accessible to developers worldwide.
489714
[discussions]: https://github.com/JnyJny/busylight/discussions
490715
[device-request]: https://github.com/JnyJny/busylight-core/issues/new?template=4_new_device_request.yaml
491716
[docs]: https://jnyjny.github.io/busylight/
717+
[docs-effects]: https://jnyjny.github.io/busylight/effects/
492718
[semver]: https://semver.org/
493719
[sphinx-docstrings]: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html
494720
[coc]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/

0 commit comments

Comments
 (0)