1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
import json
from os import path
from .matrix import Matrix, get_segment_range
from .power_calc import calcCurrent
""" import rpi_ws281x library, or import dummy version. """
try:
import rpi_ws281x as ws
except ModuleNotFoundError:
""" Dummy STRIP used for debugging purposes """
class STRIP:
def __init__(self, *args, **kwargs): pass
def begin(self, *args): pass
def blank(self): pass
def show(self): pass
def setPixelColor(self, *args): pass
def setBrightness(self, *args): pass
""" Dummy ws implementation """
class WS:
WS2812_STRIP = 1
Adafruit_NeoPixel = STRIP
def __init__(*args): print("rpi_ws281x is not installed, using a dummy module")
ws = WS()
class Strip:
""" Class representing a led-strip,
If you are using the NeoBehaviour runtime, an pre-configured instance of this will
be setup in your module with the name `strip`.
Attributes:
SEGMENTS (list[int]): A list of the configured segments.
"""
def __init__(self, strip_conf):
# Read in all config options
self.SEGMENTS = strip_conf["segments"]
self.LED_FREQ_HZ = int(strip_conf["led_freq_hz"]) # LED signal frequency in hertz (usually 800khz)
self.LED_CHANNEL = int(strip_conf["led_channel"]) # Set to '1' for GPIOs 13, 19, 41, 45, 53
self.LED_INVERT = strip_conf["led_invert"] # True to invert the signal, (when using NPN transistor level shift)
self.LED_PIN = int(strip_conf["led_pin"]) # 18 uses PWM, 10 uses SPI /dev/spidev0.0
self.LED_DMA = int(strip_conf["led_dma"]) # DMA channel for generating the signal, on the newer ones, try 10
self.LED_COUNT = sum(self.SEGMENTS) # Number of LEDs in strip
# Setup the color calibration array
if ("color_calibration" in strip_conf) and (strip_conf["color_calibration"] != ""):
self.COLOR_CALIBRATION = strip_conf["led_calibration"]
else:
self.COLOR_CALIBRATION = [0xffffffff for x in range(self.LED_COUNT)]
# Setup some buffers we can use to keep track of what will be displayed
# and what is displayed (could use rpi_ws281x functions for this maybe)
self.TMPCOLORSTATE = [0 for x in range(self.LED_COUNT)]
self.COLORSTATE = [0 for x in range(self.LED_COUNT)]
# Keeping the state of the strip power
self.__power_on = True
# Keeping what the brightness is set to
self.__set_brightness = 255
# Keeping what the brightness actually is
self.__actual_brightness = self.__set_brightness
# Setup the strip instance
self.strip = ws.Adafruit_NeoPixel(
self.LED_COUNT,
self.LED_PIN,
self.LED_FREQ_HZ,
self.LED_DMA,
self.LED_INVERT,
self.__set_brightness,
self.LED_CHANNEL,
strip_type=ws.WS2812_STRIP
)
self.strip.begin()
# Blank out all the LEDs
self.blank()
# Setup matrix
print(" * Generating matrix")
# try:
self.pixelMatrix = Matrix(self.SEGMENTS, strip_conf["matrix"])
self.pixelMatrix.dump()
# except:
# print("Something went wrong while setting up your self-defined matrix.")
# Read in state file, so we can revoces the last state.
self.__globvars_path = path.join(path.split(path.dirname(path.abspath(__file__)))[0], "state.json")
if path.exists(self.__globvars_path):
try:
with open(self.__globvars_path, "r") as f:
globvars = json.load(f)
self.power_on = globvars["power_on"]
self.brightness = globvars["brightness"]
except:
print("Could not load saved globvars...")
def save_globvars(self):
with open(self.__globvars_path, "w") as f:
f.write(json.dumps({
"power_on": self.__power_on,
"brightness": self.__set_brightness
}))
@property
def power_on(self):
""" Wether the power action is on or not. """
return self.__power_on
@power_on.setter
def power_on(self, value: bool):
""" Will set brightness to zero if value is false, or restore brightness to the last value if true. """
self.__power_on = value
self._set_brightness(self.__set_brightness if self.power_on else 0)
self.save_globvars()
@property
def brightness(self):
""" The current brightness. """
return self.__actual_brightness
@brightness.setter
def brightness(self, value: int):
""" Will set the brightness if power is on, if not it will simply save it. """
if 0 <= value <= 255:
self.__set_brightness = value
if (self.power_on):
self._set_brightness(value)
self.save_globvars()
else:
raise Exception("Value ({}) outside allowed range (0-255)".format(value))
def _set_brightness(self, value):
self.__actual_brightness = value
# Logarithmic curve, to try to make the brightness controll feel more natural.
v = int(10**((value-1)/41.11))
self.strip.setBrightness(v)
self.show()
def show(self):
"""Update the display with the data from the LED buffer, you can think of it as a flush method."""
self.COLORSTATE = self.TMPCOLORSTATE
self.strip.show()
def set_pixel_color(self, n, *color):
"""Set the color of the LED at position N.
Args:
n (int): The pixel to set.
*color: This will be interpreted as a color.
"""
if n >= self.LED_COUNT: return
c = detect_format_convert_color(*color)
self.TMPCOLORSTATE[n] = c
self.strip.setPixelColor(n, c)
# self.strip.setPixelColor(n,
# (0 << 24)
# | (int(((c & 0x00FF0000) >> 16) * ((self.COLOR_CALIBRATION[n] & 0x00FF0000) >> 16)) << 16)
# | (int(((c & 0x0000FF00) >> 8) * ((self.COLOR_CALIBRATION[n] & 0x0000FF00) >> 8)) << 8)
# | (int(((c & 0x000000FF) ) * (self.COLOR_CALIBRATION[n] & 0x000000FF) ) )
# )
def set_pixel_color_XY(self, x, y, *color):
"""Set the color of a LED at position (x, y) in the matrix you have configured to a color.
Note: This will only print a warning message if you try to set pixels outside the matrix.
Args:
x (int): The x position.
y (int): The y position.
*color: This will be interpreted as a color.
"""
try:
pixel = self.pixelMatrix.get(x, y)
self.set_pixel_color(self.pixelMatrix.get(x, y), *color)
except IndexError as e:
print(f"Pixel outside matrix cannot be set ({x}, {y})")
def set_segment_color(self, segment, *color):
"""Set a whole segment to the provided color.
Args:
segment (int): The segment to set.
*color: This will be interpreted as a color.
"""
if segment >= len(self.SEGMENTS): return
for n in get_segment_range(self.SEGMENTS, segment):
self.set_pixel_color(n, *color)
def get_pixels(self):
"""Return an object which allows access to the LED display data as if
it were a sequence of 24-bit RGB values.
"""
return self.strip.getPixels()
def num_pixels(self):
"""Return the number of pixels in the display."""
return self.LED_COUNT
def get_pixel_color(self, n):
"""Get the 24-bit RGB color value for the LED at position n."""
return self.strip.getPixelColor(n)
def blank(self, auto_show=True):
"""Will turn off all pixels.
Args:
auto_show (bool): If set to true, strip.show() is called. Disable this if needed for some quicker animations.
"""
for n in range(self.LED_COUNT):
self.set_pixel_color(n, 0)
if auto_show:
self.show()
def set_strip_color(self, *color):
"""Set all pixels in the strip to a color
Args:
*color: This will be interpreted as a color.
"""
for n in range(self.LED_COUNT):
self.set_pixel_color(n, *color)
def color_from_rgb(red, green, blue, white=0):
"""
Convert the provided red, green, blue color to a 24-bit color value.
Each color component should be a value 0-255
where 0 is the lowest intensity and 255 is the highest intensity.
"""
return (white << 24) | (red << 16) | (green << 8) | blue
def color_from_hex(hex_color: str):
""" Convert the provided hex code to a 24-bit color value. """
value = hex_color.lstrip('#')
lv = len(value)
rgb = tuple(int(value[i:i+lv//3], 16) for i in range(0, lv, lv//3))
return color_from_rgb(red=rgb[0], green=rgb[1], blue=rgb[2])
def detect_format_convert_color(*color) -> int:
"""
Detect format of a color and return its 24-bit color value.
If parameter is only a str, it will be treated as a hex value.
If parameter is a tuple, the first three items in that tuple will be treated as a rgb value.
If parameter is a int, it will be treated as a 24-bit color value.
If there are 3 parameters, these will be treated as a rgb value.
"""
if (len(color) == 1) and (isinstance(color[0], str)):
return color_from_hex(color[0])
if (len(color) == 1) and (isinstance(color[0], tuple)):
return color_from_rgb(*(color[0]))
if (len(color) == 1) and (isinstance(color[0], int)):
return color[0]
if (len(color) == 3):
return color_from_rgb(*color)
raise ValueError("Invalid parameters provided, check documentation.")
|