forked from pvx/litsimaja
Compare commits
17 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
545ff966c8 | 2 months ago |
|
|
33109455c9 | 5 years ago |
|
|
103e04a1b4 | 5 years ago |
|
|
86dd419080 | 5 years ago |
|
|
0a5e1a3a5d | 5 years ago |
|
|
74b6cbe7e3 | 5 years ago |
|
|
e6fb8ea528 | 5 years ago |
|
|
0d59414104 | 5 years ago |
|
|
3fa0bc376f | 5 years ago |
|
|
b3240631f6 | 5 years ago |
|
|
76759f986b | 5 years ago |
|
|
57559ea70a | 5 years ago |
|
|
1b3b23bc99 | 5 years ago |
|
|
d135c8c24f | 5 years ago |
|
|
bdcfdb9a9f | 5 years ago |
|
|
810fefe323 | 5 years ago |
|
|
4be70b9c31 | 5 years ago |
16 changed files with 453 additions and 72 deletions
@ -0,0 +1,17 @@ |
|||||||
|
ENVIRONMENT=prod |
||||||
|
USE_EMULATOR=false |
||||||
|
BIND_ADDR=127.0.0.1 |
||||||
|
BIND_PORT=8080 |
||||||
|
|
||||||
|
# Strip configuration |
||||||
|
STRIP_PIXELS=290 |
||||||
|
STRIP_GPIO_PIN=18 |
||||||
|
STRIP_HZ=800000 |
||||||
|
STRIP_DMA=10 |
||||||
|
STRIP_INVERT=false |
||||||
|
STRIP_BRIGHTNESS=255 |
||||||
|
STRIP_CHANNEL=0 |
||||||
|
STRIP_TYPE=4104 |
||||||
|
|
||||||
|
REGIONS=46,96,191,241 |
||||||
|
BPM_DEFAULT=60 |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
ENVIRONMENT=dev |
||||||
|
USE_EMULATOR=true |
||||||
@ -1 +1,2 @@ |
|||||||
|
.env |
||||||
litsimaja.log |
litsimaja.log |
||||||
|
|||||||
@ -0,0 +1,38 @@ |
|||||||
|
from dotenv import dotenv_values |
||||||
|
import pathlib |
||||||
|
|
||||||
|
|
||||||
|
def populate_values(load: {}): |
||||||
|
conf = load |
||||||
|
conf['IS_DEV']: bool = str.lower(load['ENVIRONMENT']) == 'dev' |
||||||
|
conf['USE_EMULATOR']: bool = str.lower(load['USE_EMULATOR']) == 'true' |
||||||
|
conf['BIND_PORT']: int = int(load['BIND_PORT']) |
||||||
|
|
||||||
|
conf['STRIP_PIXELS']: int = int(load['STRIP_PIXELS']) |
||||||
|
conf['STRIP_GPIO_PIN']: int = int(load['STRIP_GPIO_PIN']) |
||||||
|
conf['STRIP_HZ']: int = int(load['STRIP_HZ']) |
||||||
|
conf['STRIP_DMA']: int = int(load['STRIP_DMA']) |
||||||
|
conf['STRIP_INVERT']: bool = str.lower(load['STRIP_INVERT']) == 'true' |
||||||
|
conf['STRIP_BRIGHTNESS']: int = int(load['STRIP_BRIGHTNESS']) |
||||||
|
conf['STRIP_CHANNEL']: int = int(load['STRIP_CHANNEL']) |
||||||
|
conf['STRIP_TYPE']: int = int(load['STRIP_TYPE']) |
||||||
|
|
||||||
|
conf['REGIONS']: [] = [int(x) for x in load['REGIONS'].split(',')] |
||||||
|
conf['BPM_DEFAULT']: int = int(load['BPM_DEFAULT']) |
||||||
|
|
||||||
|
return conf |
||||||
|
|
||||||
|
|
||||||
|
class Config(object): |
||||||
|
_config: {} = {} |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
rp = str(pathlib.Path(__file__).parent.parent.absolute()) |
||||||
|
load_conf = { |
||||||
|
**dotenv_values(rp + "/.env.defaults"), |
||||||
|
**dotenv_values(rp + "/.env"), |
||||||
|
} |
||||||
|
self._config = populate_values(load_conf) |
||||||
|
|
||||||
|
def get(self, key: str): |
||||||
|
return self._config[key] |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
class Regions(object): |
||||||
|
_pixelsEnabled: [] = [] |
||||||
|
_regions: [] = [] |
||||||
|
|
||||||
|
def __init__(self, strip_length: int, splitters: []): |
||||||
|
self._length: int = strip_length |
||||||
|
start_pixel = 0 |
||||||
|
for i in splitters: |
||||||
|
if i > self._length: |
||||||
|
raise ValueError('splitter out of bounds, you idiot') |
||||||
|
self._regions.append([start_pixel, i + 1, True]) |
||||||
|
start_pixel = i + 1 |
||||||
|
|
||||||
|
self._regions.append([start_pixel, self._length, True]) |
||||||
|
for i in range(self._length): |
||||||
|
self._pixelsEnabled.append(True) |
||||||
|
|
||||||
|
def region_switch(self, region_id: int, enabled: bool): |
||||||
|
pixel_region = self._regions[region_id] |
||||||
|
for i in range(pixel_region[0], pixel_region[1]): |
||||||
|
self._pixelsEnabled[i] = enabled |
||||||
|
self._regions[region_id][2] = enabled |
||||||
|
|
||||||
|
def is_pixel_enabled(self, pixel_id: int) -> bool: |
||||||
|
return self._pixelsEnabled[pixel_id] |
||||||
|
|
||||||
|
def is_region_enabled(self, region_id: int) -> bool: |
||||||
|
return self._regions[region_id][2] |
||||||
|
|
||||||
|
def switch_region(self, region_id: int): |
||||||
|
status: bool = self._regions[region_id][2] |
||||||
|
self.region_switch(region_id, not status) |
||||||
|
|
||||||
|
def list_region_ids(self): |
||||||
|
result = [] |
||||||
|
for i in range(len(self._regions)): |
||||||
|
result.append(i) |
||||||
|
return result |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
import time |
||||||
|
from typing import Optional |
||||||
|
|
||||||
|
|
||||||
|
class Tempo(object): |
||||||
|
_last_beat: float |
||||||
|
_override_beat: Optional[float] = None |
||||||
|
|
||||||
|
def __init__(self, bpm: float = 60): |
||||||
|
self._bpm: float = bpm |
||||||
|
self.update_last_beat() |
||||||
|
|
||||||
|
def set_bpm(self, bpm: float) -> None: |
||||||
|
self._bpm = bpm |
||||||
|
|
||||||
|
def get_bpm(self) -> float: |
||||||
|
return self._bpm |
||||||
|
|
||||||
|
def update_last_beat(self) -> None: |
||||||
|
self._last_beat = time.time() |
||||||
|
|
||||||
|
def get_beat_time(self) -> float: |
||||||
|
return 1000 / (self._bpm / 60) |
||||||
|
|
||||||
|
def get_beat_delay(self) -> Optional[float]: |
||||||
|
tick = self._override_beat |
||||||
|
if tick is None: |
||||||
|
return None |
||||||
|
self._override_beat = None |
||||||
|
return tick - self._last_beat |
||||||
|
|
||||||
|
def sync_beat(self): |
||||||
|
self._override_beat = time.time() |
||||||
|
|
||||||
|
def wait(self, beat_divider: int = 1): |
||||||
|
now = time.time() |
||||||
|
next_beat = self._last_beat + (self.get_beat_time() / beat_divider / 1000) |
||||||
|
wait_time = next_beat - now |
||||||
|
beat_delay = self.get_beat_delay() |
||||||
|
if beat_delay is not None: |
||||||
|
wait_time += beat_delay |
||||||
|
if wait_time > 0: |
||||||
|
time.sleep(wait_time) |
||||||
|
self.update_last_beat() |
||||||
|
return True |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
from lib.Program import Program |
||||||
|
from rpi_ws281x import Color |
||||||
|
import time |
||||||
|
|
||||||
|
def name(): |
||||||
|
return 'Christmas Lights' |
||||||
|
|
||||||
|
class ChristmasLights(Program): |
||||||
|
def run(self, args: [] = None): |
||||||
|
# Configuration |
||||||
|
wait_ms = 0 # Speed of the animation |
||||||
|
|
||||||
|
# Define classic festive colors |
||||||
|
RED = Color(255, 0, 0) |
||||||
|
GREEN = Color(0, 255, 0) |
||||||
|
WARM_WHITE = Color(200, 180, 60) # A golden-ish warm white |
||||||
|
|
||||||
|
colors = [RED, GREEN, WARM_WHITE] |
||||||
|
|
||||||
|
# This determines how many pixels of the same color are next to each other |
||||||
|
group_size = 2 |
||||||
|
|
||||||
|
offset = 0 |
||||||
|
while self.get_loop().status(): |
||||||
|
num_pixels = self._lm.count_pixels() |
||||||
|
|
||||||
|
for i in range(num_pixels): |
||||||
|
# Calculate which color index to use based on pixel position and current offset |
||||||
|
# This creates the "moving" effect |
||||||
|
color_index = ((i + offset) // group_size) % len(colors) |
||||||
|
self._lm.set_pixel_color(i, colors[color_index]) |
||||||
|
|
||||||
|
self._lm.show() |
||||||
|
|
||||||
|
# Increment offset to move the lights |
||||||
|
offset = (offset + 1) % (len(colors) * group_size) |
||||||
|
self._lm.get_tempo().wait() |
||||||
|
time.sleep(wait_ms / 1000.0) |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
from lib.Program import Program |
||||||
|
from rpi_ws281x import Color |
||||||
|
import time |
||||||
|
|
||||||
|
|
||||||
|
def name(): |
||||||
|
return 'RGBT Gaynbow' |
||||||
|
|
||||||
|
|
||||||
|
def wheel(pos): |
||||||
|
"""Generate rainbow colors across 0-255 positions.""" |
||||||
|
if pos < 85: |
||||||
|
return Color(pos * 3, 255 - pos * 3, 0) |
||||||
|
elif pos < 170: |
||||||
|
pos -= 85 |
||||||
|
return Color(255 - pos * 3, 0, pos * 3) |
||||||
|
else: |
||||||
|
pos -= 170 |
||||||
|
return Color(0, pos * 3, 255 - pos * 3) |
||||||
|
|
||||||
|
|
||||||
|
class Gaynbow(Program): |
||||||
|
def run(self, args: [] = None): |
||||||
|
wait_ms = 20 |
||||||
|
iterations = 5 |
||||||
|
while self.get_loop().status(): |
||||||
|
"""Draw rainbow that uniformly distributes itself across all pixels.""" |
||||||
|
for j in range(256 * iterations): |
||||||
|
if not self.get_loop().status(): |
||||||
|
break |
||||||
|
for i in range(self._lm.count_pixels()): |
||||||
|
self._lm.set_pixel_color(i, wheel( |
||||||
|
(int(i * 256 / self._lm.count_pixels()) + j) & 255)) |
||||||
|
self._lm.show() |
||||||
|
time.sleep(wait_ms / 1000.0) |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
from lib.Program import Program |
||||||
|
import time |
||||||
|
|
||||||
|
|
||||||
|
def name(): |
||||||
|
return '1Hz tick' |
||||||
|
|
||||||
|
|
||||||
|
# Tick seconds like analog clock |
||||||
|
class HzTick(Program): |
||||||
|
def run(self, args: [] = None) -> None: |
||||||
|
tempo = self._lm.get_tempo() |
||||||
|
|
||||||
|
while self.get_loop().status(): |
||||||
|
second = time.localtime().tm_sec |
||||||
|
second_pixel = int(self._lm.count_pixels() / 60 * second) |
||||||
|
color_arr = args['color'] |
||||||
|
for i in range(self._lm.count_pixels()): |
||||||
|
self._lm.set_pixel_color(i, (color_arr[0] << 16) | (color_arr[1] << 8) | color_arr[2]) |
||||||
|
self._lm.set_pixel_color(second_pixel, 255) |
||||||
|
self._lm.show() |
||||||
|
tempo.wait() |
||||||
Loading…
Reference in new issue