# Pattern Contract (Important) Pattern classes are loaded dynamically by `Presets._load_dynamic_patterns()`. Patterns must follow this contract exactly. ## Required class shape - File name is the pattern id (for example `blink.py` -> pattern name `blink`). - Module exports a class with: - `__init__(self, driver)` where `driver` is the `Presets` instance. - `run(self, preset)` that returns a generator. `Presets` binds patterns like this: - `pattern_class(self).run` - then calls `self.patterns[preset.p](preset)` and stores that generator. - every frame, `Presets.tick()` does `next(self.generator)`. ## `run()` generator rules - `run()` must `yield` frequently (normally once per tick loop). - Do not block inside `run()`: - no `sleep()` / `sleep_ms()` / long loops without `yield`. - no network or file I/O. - Use time checks (`utime.ticks_ms()` + `utime.ticks_diff(...)`) to schedule updates. - Keep pattern state inside local variables in `run()` (or object fields if needed). ## Drawing and brightness - Use `self.driver.apply_brightness(color, preset.b)` for per-preset brightness. - Write pixels through `self.driver.n[...]` / `self.driver.n.fill(...)`. - Flush frame with `self.driver.n.write()`. - If a pattern needs to clear, use black `(0, 0, 0)`. ## Step semantics - `self.driver.step` is shared pattern state managed by `Presets.select(...)` and patterns. - Patterns that use step-based progression should update `self.driver.step` themselves. - `select(..., step=...)` may set an explicit starting step. ## Error handling - Let unexpected errors raise inside the generator. - `Presets.tick()` catches exceptions, logs, and stops the active generator. - Pattern code should not swallow broad exceptions unless there is a clear recovery path. ## Built-ins - `off` and `on` are built-in methods on `Presets`, not loaded from this folder. - `__init__.py` is ignored by dynamic loader.