1
0

segment.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/usr/bin/env python3
  2. """
  3. WLED Segment class for Raspberry Pi
  4. Manages LED strip segments and their effects
  5. """
  6. import time
  7. from typing import List, Callable, Optional
  8. from .utils.colors import *
  9. from .utils.palettes import color_from_palette, get_palette
  10. class Segment:
  11. """LED segment with effect support"""
  12. def __init__(self, pixels, start: int, stop: int):
  13. """
  14. Initialize a segment
  15. pixels: neopixel.NeoPixel object
  16. start: starting LED index
  17. stop: ending LED index (exclusive)
  18. """
  19. self.pixels = pixels
  20. self.start = start
  21. self.stop = stop
  22. self.length = stop - start
  23. # Colors (up to 3 colors like WLED)
  24. self.colors = [0x00FF0000, 0x000000FF, 0x0000FF00] # Red, Blue, Green defaults
  25. # Effect parameters
  26. self.speed = 128 # 0-255
  27. self.intensity = 128 # 0-255
  28. self.palette_id = 0 # Palette ID
  29. self.custom1 = 0 # Custom parameter 1
  30. self.custom2 = 0 # Custom parameter 2
  31. self.custom3 = 0 # Custom parameter 3
  32. # Runtime state (like SEGENV in WLED)
  33. self.call = 0 # Number of times effect has been called
  34. self.step = 0 # Effect step counter
  35. self.aux0 = 0 # Auxiliary variable 0
  36. self.aux1 = 0 # Auxiliary variable 1
  37. self.next_time = 0 # Next time to run effect (ms)
  38. self.data = [] # Effect data storage
  39. # Timing
  40. self._start_time = time.time() * 1000 # Convert to milliseconds
  41. def get_pixel_color(self, i: int) -> int:
  42. """Get color of pixel at index i (segment-relative)"""
  43. if i < 0 or i >= self.length:
  44. return 0
  45. actual_idx = self.start + i
  46. color = self.pixels[actual_idx]
  47. # Convert from neopixel (R,G,B) or (G,R,B) to 32-bit
  48. if isinstance(color, tuple):
  49. if len(color) == 3:
  50. return (color[0] << 16) | (color[1] << 8) | color[2]
  51. elif len(color) == 4:
  52. return (color[3] << 24) | (color[0] << 16) | (color[1] << 8) | color[2]
  53. return 0
  54. def set_pixel_color(self, i: int, color: int):
  55. """Set color of pixel at index i (segment-relative)"""
  56. if i < 0 or i >= self.length:
  57. return
  58. actual_idx = self.start + i
  59. r = get_r(color)
  60. g = get_g(color)
  61. b = get_b(color)
  62. self.pixels[actual_idx] = (r, g, b)
  63. def fill(self, color: int):
  64. """Fill entire segment with color"""
  65. for i in range(self.length):
  66. self.set_pixel_color(i, color)
  67. def fade_out(self, amount: int):
  68. """Fade all pixels in segment toward black"""
  69. for i in range(self.length):
  70. current = self.get_pixel_color(i)
  71. faded = color_fade(current, amount)
  72. self.set_pixel_color(i, faded)
  73. def blur(self, amount: int):
  74. """Blur/blend pixels with neighbors"""
  75. if amount == 0 or self.length < 3:
  76. return
  77. # Simple blur: blend each pixel with neighbors
  78. temp = [self.get_pixel_color(i) for i in range(self.length)]
  79. for i in range(self.length):
  80. if i == 0:
  81. # First pixel: blend with next
  82. blended = color_blend(temp[i], temp[i + 1], amount)
  83. elif i == self.length - 1:
  84. # Last pixel: blend with previous
  85. blended = color_blend(temp[i], temp[i - 1], amount)
  86. else:
  87. # Middle pixels: blend with both neighbors
  88. left = color_blend(temp[i], temp[i - 1], amount // 2)
  89. blended = color_blend(left, temp[i + 1], amount // 2)
  90. self.set_pixel_color(i, blended)
  91. def now(self) -> int:
  92. """Get current time in milliseconds since segment start"""
  93. return int((time.time() * 1000) - self._start_time)
  94. def color_from_palette(self, index: int, use_index: bool = True,
  95. brightness: int = 255) -> int:
  96. """
  97. Get color from current palette
  98. index: LED index or palette position
  99. use_index: if True, scale index across palette
  100. brightness: 0-255
  101. """
  102. palette = get_palette(self.palette_id)
  103. if use_index and self.length > 1:
  104. # Map LED position to palette position
  105. palette_pos = (index * 255) // (self.length - 1)
  106. else:
  107. palette_pos = index & 0xFF
  108. return color_from_palette(palette, palette_pos, brightness)
  109. def get_color(self, index: int) -> int:
  110. """Get segment color by index (0-2)"""
  111. if 0 <= index < len(self.colors):
  112. return self.colors[index]
  113. return 0
  114. def reset(self):
  115. """Reset segment state"""
  116. self.call = 0
  117. self.step = 0
  118. self.aux0 = 0
  119. self.aux1 = 0
  120. self.next_time = 0
  121. self.data = []