renaming folders and files to python format
This commit is contained in:
parent
ade378d128
commit
798775805c
@ -18,7 +18,6 @@ class ThreadedUDPServer(threading.Thread):
|
|||||||
self.start()
|
self.start()
|
||||||
self.udpClientGuardian = self.UDPClientGuardian()
|
self.udpClientGuardian = self.UDPClientGuardian()
|
||||||
self.udpClientGuardian.start()
|
self.udpClientGuardian.start()
|
||||||
UDPClients
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -86,13 +85,6 @@ class UDPClient():
|
|||||||
self.lastping = time()
|
self.lastping = time()
|
||||||
|
|
||||||
def handle(self, request):
|
def handle(self, request):
|
||||||
|
|
||||||
clientdata = request[0].decode()
|
|
||||||
self.socket = request[1]
|
|
||||||
#print(time(),"{} wrote:".format(self.client_address))
|
|
||||||
#print(time(),"clientdata -> ", clientdata)
|
|
||||||
# socket.sendto(bytes("pong","utf-8"),self.client_address)
|
|
||||||
|
|
||||||
# Client Types:
|
# Client Types:
|
||||||
# CLIENT_TYPE_CONTROLLER = 0
|
# CLIENT_TYPE_CONTROLLER = 0
|
||||||
# CLIENT_TYPE_STRIPE = 1
|
# CLIENT_TYPE_STRIPE = 1
|
||||||
@ -104,8 +96,9 @@ class UDPClient():
|
|||||||
# s:ping
|
# s:ping
|
||||||
# UDP
|
# UDP
|
||||||
|
|
||||||
|
self.socket = request[1]
|
||||||
try:
|
try:
|
||||||
data = clientdata.split(':')
|
data = request[0].decode().split(':')
|
||||||
#print(data)
|
#print(data)
|
||||||
# r:1:srg strip name
|
# r:1:srg strip name
|
||||||
if data[0] == "r" and int(data[1]) == CLIENT_TYPE_STRIPE and data[2] != None and self.registered is False:
|
if data[0] == "r" and int(data[1]) == CLIENT_TYPE_STRIPE and data[2] != None and self.registered is False:
|
||||||
|
@ -22,8 +22,8 @@ class ThreadedWebSocketServer(Thread):
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from rgbUtils import effectControllerJsonHelper
|
from BackendProvider.Helper import effectControllerJsonHelper
|
||||||
from rgbUtils import rgbStripControllerJsonHelper
|
from BackendProvider.Helper import rgbStripControllerJsonHelper
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
@ -63,12 +63,6 @@ class HTTPWebSocketsHandler(WebSocket):
|
|||||||
ledcount=1
|
ledcount=1
|
||||||
if "led_count" in data:
|
if "led_count" in data:
|
||||||
ledcount = int(data["led_count"])
|
ledcount = int(data["led_count"])
|
||||||
self.nojson=0
|
|
||||||
if "nojson" in data:
|
|
||||||
self.nojson = int(data["nojson"])
|
|
||||||
self.nosend=0
|
|
||||||
if "nosend" in data:
|
|
||||||
self.nosend = int(data["nosend"])
|
|
||||||
|
|
||||||
# registers the strip with websocket object and name. the onRGBStripValueUpdate(rgbStrip) is called by
|
# registers the strip with websocket object and name. the onRGBStripValueUpdate(rgbStrip) is called by
|
||||||
# by the rgbStrip when an effectThread updates it
|
# by the rgbStrip when an effectThread updates it
|
||||||
@ -87,13 +81,9 @@ class HTTPWebSocketsHandler(WebSocket):
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# the stripe should usualy not send any data, i do not know why it should...
|
# the stripe should usualy not send any data, i do not know why it should...
|
||||||
elif self.client_type is CLIENT_TYPE_STRIPE:
|
elif self.client_type is CLIENT_TYPE_STRIPE:
|
||||||
if self.nosend == 1:
|
|
||||||
respdata = "d"
|
|
||||||
for i in range(self.rgbStrip.STRIP_LENGHT):
|
|
||||||
respdata += ":" + str(i) + ":"+str(self.rgbStrip.red[i])+":"+str(self.rgbStrip.green[i])+":"+str(self.rgbStrip.blue[i])
|
|
||||||
self.sendMessage(respdata)
|
|
||||||
return
|
return
|
||||||
# audio recorder responses are handled by the effectControllerJsonHandler
|
# audio recorder responses are handled by the effectControllerJsonHandler
|
||||||
elif self.client_type is CLIENT_TYPE_RECORDER:
|
elif self.client_type is CLIENT_TYPE_RECORDER:
|
||||||
@ -139,15 +129,8 @@ class HTTPWebSocketsHandler(WebSocket):
|
|||||||
|
|
||||||
# when a rgbStrip value is changed, send json data to client
|
# when a rgbStrip value is changed, send json data to client
|
||||||
def onRGBStripValueUpdate(self,rgbStrip):
|
def onRGBStripValueUpdate(self,rgbStrip):
|
||||||
if self.nosend == 0:
|
|
||||||
if self.nojson == 0:
|
|
||||||
self.sendMessage(
|
self.sendMessage(
|
||||||
json.dumps({
|
json.dumps({
|
||||||
'data': rgbStripControllerJsonHelper.getRGBData(rgbStrip)
|
'data': rgbStripControllerJsonHelper.getRGBData(rgbStrip)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
if self.nojson == 1:
|
|
||||||
respdata = "d"
|
|
||||||
for i in range(rgbStrip.STRIP_LENGHT):
|
|
||||||
respdata += ":" + str(i) + ":"+str(rgbStrip.red[i])+":"+str(rgbStrip.green[i])+":"+str(rgbStrip.blue[i])
|
|
||||||
self.sendMessage(respdata)
|
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
from rgbUtils.BaseEffect import BaseEffect
|
from Utils.BaseEffect import BaseEffect
|
||||||
from rgbUtils.EffectParameter import slider, colorpicker
|
from Utils.EffectParameter import slider, colorpicker
|
||||||
from rgbUtils.debug import debug
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import numpy
|
import numpy
|
||||||
from rgbUtils.pyAudioRecorder import pyAudioRecorder
|
|
||||||
from time import perf_counter, sleep
|
from time import perf_counter, sleep
|
||||||
from random import randint, shuffle
|
from random import randint, shuffle
|
||||||
|
|
||||||
class musikEffect(BaseEffect):
|
class MusikEffect(BaseEffect):
|
||||||
name = "musikEffect"
|
name = "musikEffect"
|
||||||
desc = "LED-Band *sollte* nach musik blinken"
|
desc = "LED-Band *sollte* nach musik blinken"
|
||||||
|
|
||||||
@ -18,7 +16,7 @@ class musikEffect(BaseEffect):
|
|||||||
# Something that will be used to show descriptions and value options
|
# Something that will be used to show descriptions and value options
|
||||||
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
||||||
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
||||||
effectParameters = [
|
effectParameters: list = [
|
||||||
# radio(\
|
# radio(\
|
||||||
# "Shuffle LED to Freq Order",\
|
# "Shuffle LED to Freq Order",\
|
||||||
# "Off -> Mapping ",\
|
# "Off -> Mapping ",\
|
||||||
@ -42,12 +40,14 @@ class musikEffect(BaseEffect):
|
|||||||
self.fft_random_keys = [0,1,2,3]
|
self.fft_random_keys = [0,1,2,3]
|
||||||
self.fft_random = [0,0,0,0]
|
self.fft_random = [0,0,0,0]
|
||||||
|
|
||||||
|
self.y_max_freq_avg_list = []
|
||||||
|
self.low_freq_avg_list = []
|
||||||
|
self.bpm_list = []
|
||||||
|
self.prev_beat = 0 # timestamp
|
||||||
|
|
||||||
# used by strobe()
|
# used by strobe()
|
||||||
self.lastmode = 0
|
self.lastmode = 0
|
||||||
|
|
||||||
self.recorderClient = pyAudioRecorder.recorderClient()
|
|
||||||
self.rgbStripController.pyAudioRecorder.registerRecorderClient(self.recorderClient)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
#loop effect as long as not stopped
|
#loop effect as long as not stopped
|
||||||
@ -67,17 +67,17 @@ class musikEffect(BaseEffect):
|
|||||||
# b=0
|
# b=0
|
||||||
# RGBStrip.RGB(r,g,b)
|
# RGBStrip.RGB(r,g,b)
|
||||||
#self.lastTime = time.time()
|
#self.lastTime = time.time()
|
||||||
sleep(.001)
|
sleep(0.00833333333)
|
||||||
|
|
||||||
def end(self):
|
def end(self):
|
||||||
self.rgbStripController.pyAudioRecorder.unregisterRecorderClient(self.recorderClient)
|
pass
|
||||||
|
|
||||||
def plot_audio_and_detect_beats(self):
|
def plot_audio_and_detect_beats(self):
|
||||||
if not self.rgbStripController.pyAudioRecorder.has_new_audio:
|
if not self.hasNewFttValues:
|
||||||
return
|
return
|
||||||
|
|
||||||
# get x and y values from FFT
|
# get x and y values from FFT
|
||||||
xs, ys = self.rgbStripController.pyAudioRecorder.fft()
|
xs, ys = self.ftt
|
||||||
|
|
||||||
# calculate average for all frequency ranges
|
# calculate average for all frequency ranges
|
||||||
y_avg = numpy.mean(ys)
|
y_avg = numpy.mean(ys)
|
||||||
@ -88,9 +88,9 @@ class musikEffect(BaseEffect):
|
|||||||
#mid_freq = numpy.mean(ys[88:115])
|
#mid_freq = numpy.mean(ys[88:115])
|
||||||
#hig_freq = numpy.mean(ys[156:184])
|
#hig_freq = numpy.mean(ys[156:184])
|
||||||
|
|
||||||
low_freq = numpy.mean(ys[0:67])
|
low_freq = numpy.mean(ys[0:int(ys.__len__() / 3 * 1)])
|
||||||
mid_freq = numpy.mean(ys[68:135])
|
mid_freq = numpy.mean(ys[int(ys.__len__() / 3 * 1):int(ys.__len__() / 3 * 2)])
|
||||||
hig_freq = numpy.mean(ys[136:204])
|
hig_freq = numpy.mean(ys[int(ys.__len__() / 3 * 2):int(ys.__len__() / 3 * 3)])
|
||||||
|
|
||||||
#get the maximum of all freq
|
#get the maximum of all freq
|
||||||
if len(self.y_max_freq_avg_list) < 250 or y_avg > 10 and numpy.amax([low_freq,mid_freq,hig_freq])/2 > numpy.amin(self.y_max_freq_avg_list):
|
if len(self.y_max_freq_avg_list) < 250 or y_avg > 10 and numpy.amax([low_freq,mid_freq,hig_freq])/2 > numpy.amin(self.y_max_freq_avg_list):
|
||||||
@ -166,6 +166,16 @@ class musikEffect(BaseEffect):
|
|||||||
self.fft_random[1],
|
self.fft_random[1],
|
||||||
self.fft_random[2]
|
self.fft_random[2]
|
||||||
)
|
)
|
||||||
|
"""
|
||||||
|
rgbStrip: RGBStrip
|
||||||
|
for rgbStrip in self.effectRGBStrips():
|
||||||
|
for i in range(rgbStrip.STRIP_LENGHT):
|
||||||
|
if rgbStrip.STRIP_LENGHT-1-10 is not i:
|
||||||
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, rgbStrip.red[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.green[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.blue[rgbStrip.STRIP_LENGHT-i-2])
|
||||||
|
else:
|
||||||
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, self.fft_random[0], self.fft_random[1], self.fft_random[2])
|
||||||
|
rgbStrip.show()
|
||||||
|
"""
|
||||||
|
|
||||||
# shorten the cumulative list to account for changes in dynamics
|
# shorten the cumulative list to account for changes in dynamics
|
||||||
if len(self.low_freq_avg_list) > 500:
|
if len(self.low_freq_avg_list) > 500:
|
||||||
@ -187,8 +197,7 @@ class musikEffect(BaseEffect):
|
|||||||
self.low_freq_avg_list = []
|
self.low_freq_avg_list = []
|
||||||
print("new song")
|
print("new song")
|
||||||
self.off()
|
self.off()
|
||||||
|
self.hasNewFttValues = False
|
||||||
self.rgbStripController.pyAudioRecorder.newAudio = False
|
|
||||||
# print(self.bpm_list)250
|
# print(self.bpm_list)250
|
||||||
|
|
||||||
def strobe(self):
|
def strobe(self):
|
184
server/Effects/MusikEffect2.py
Normal file
184
server/Effects/MusikEffect2.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
from Utils.BaseEffect import BaseEffect
|
||||||
|
from Utils.RGBStrip import RGBStrip
|
||||||
|
from Utils.EffectParameter import slider, colorpicker
|
||||||
|
import time
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import numpy
|
||||||
|
from time import perf_counter, sleep
|
||||||
|
from random import randint, shuffle
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
#matplotlib.use('TkAgg') # THIS MAKES IT FAST!
|
||||||
|
matplotlib.get_backend()
|
||||||
|
|
||||||
|
class MusikEffect2(BaseEffect):
|
||||||
|
name = "musikEffect2"
|
||||||
|
desc = "LED-Band *sollte* nach musik blinken"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Something that will be used to show descriptions and value options
|
||||||
|
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
||||||
|
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
||||||
|
"""radio(
|
||||||
|
"Shuffle LED to Freq Order",
|
||||||
|
"Off -> Mapping ",
|
||||||
|
[
|
||||||
|
[0,255,255,"red"],
|
||||||
|
[0,255,255,"green"],
|
||||||
|
[0,255,255,"blue"]
|
||||||
|
]
|
||||||
|
),"""
|
||||||
|
effectParameters: list = [
|
||||||
|
slider(
|
||||||
|
"Effect Brightnes",
|
||||||
|
"Choose a brightness for your LED's",
|
||||||
|
[
|
||||||
|
[0,100,100,"brightness"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
|
||||||
|
|
||||||
|
self.fft_random_keys = [0,1,2,3]
|
||||||
|
self.fft_random = [0,0,0,0]
|
||||||
|
|
||||||
|
self.y_max_freq_avg_list = [0]
|
||||||
|
self.low_freq_avg_list = [0]
|
||||||
|
self.bpm_list = []
|
||||||
|
self.prev_beat = 0 # timestamp
|
||||||
|
|
||||||
|
'''
|
||||||
|
self.fig = plt.gcf()
|
||||||
|
|
||||||
|
self.fig.show()
|
||||||
|
'''
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
#loop effect as long as not stopped
|
||||||
|
def effect(self):
|
||||||
|
'''plt.clf()
|
||||||
|
#plt.ylim(-.5, numpy.amax(self.y_max_freq_avg_list))
|
||||||
|
plt.xlabel('xs')
|
||||||
|
plt.ylabel('ys')
|
||||||
|
plt.plot(self.ftt[0],self.ftt[1])
|
||||||
|
self.fig.canvas.draw()
|
||||||
|
self.fig.canvas.flush_events()'''
|
||||||
|
self.plot_audio_and_detect_beats()
|
||||||
|
#self.freqtocolor()
|
||||||
|
#if(time.time() - self.lastTime >= 0.002):
|
||||||
|
#for RGBStrip in self.effectRGBStrips():
|
||||||
|
# r= RGBStrip.red-1
|
||||||
|
# if(r<0):
|
||||||
|
# r=0
|
||||||
|
# g= RGBStrip.green-1
|
||||||
|
# if(g<0):
|
||||||
|
# g=0
|
||||||
|
# b= RGBStrip.blue-1
|
||||||
|
# if(b<0):
|
||||||
|
# b=0
|
||||||
|
# RGBStrip.RGB(r,g,b)
|
||||||
|
#self.lastTime = time.time()
|
||||||
|
sleep(0.00833333333)
|
||||||
|
|
||||||
|
def end(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def plot_audio_and_detect_beats(self):
|
||||||
|
if not self.hasNewFttValues:
|
||||||
|
return
|
||||||
|
|
||||||
|
# get x and y values from FFT
|
||||||
|
xs, ys = self.ftt
|
||||||
|
|
||||||
|
amax = numpy.amax(ys)
|
||||||
|
if amax > 0:
|
||||||
|
self.y_max_freq_avg_list.append(amax)
|
||||||
|
if len(self.y_max_freq_avg_list ) > 50000:
|
||||||
|
self.y_max_freq_avg_list = self.y_max_freq_avg_list[25000:]
|
||||||
|
|
||||||
|
calc = numpy.amax(ys) / numpy.amax(self.y_max_freq_avg_list) * 765
|
||||||
|
"""print(amax)
|
||||||
|
print(numpy.amax(self.y_max_freq_avg_list))
|
||||||
|
print(calc)
|
||||||
|
print("-----------")
|
||||||
|
"""
|
||||||
|
c: self.Color = self.pitchRainbow(calc)
|
||||||
|
rgbStrip: RGBStrip
|
||||||
|
for rgbStrip in self.effectRGBStrips():
|
||||||
|
"""
|
||||||
|
for i in range(rgbStrip.STRIP_LENGHT):
|
||||||
|
if rgbStrip.STRIP_LENGHT-1-10 is not i:
|
||||||
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, rgbStrip.red[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.green[rgbStrip.STRIP_LENGHT-i-2], rgbStrip.blue[rgbStrip.STRIP_LENGHT-i-2])
|
||||||
|
else:
|
||||||
|
rgbStrip.WS2812b(rgbStrip.STRIP_LENGHT-i-1, c.red, c.green, c.blue)
|
||||||
|
rgbStrip.show()
|
||||||
|
"""
|
||||||
|
rgbStrip.RGB(int(c.red),int(c.green),int(c.blue))
|
||||||
|
# shorten the cumulative list to account for changes in dynamics
|
||||||
|
|
||||||
|
|
||||||
|
def pitchRainbow(self,p: float):
|
||||||
|
if p < 1:
|
||||||
|
return self.Color(0,0,0)
|
||||||
|
elif p < 255:
|
||||||
|
return self.Color(255 - p % 255,p % 255,0)
|
||||||
|
elif p < 510:
|
||||||
|
return self.Color(0,255 - p % 255,p % 255)
|
||||||
|
else:
|
||||||
|
return self.Color(p % 255,0,255 - p % 255)
|
||||||
|
|
||||||
|
def pitchConv(self,p: float):
|
||||||
|
|
||||||
|
r:float
|
||||||
|
g:float
|
||||||
|
b:float
|
||||||
|
|
||||||
|
if p < 40:
|
||||||
|
return self.Color(255,0,0)
|
||||||
|
|
||||||
|
elif p >= 40 and p <= 77 :
|
||||||
|
b = (p - 40) * (255/37.0000)
|
||||||
|
return self.Color(255,0,b)
|
||||||
|
|
||||||
|
elif p > 77 and p <= 205:
|
||||||
|
r = 255 - ((p - 78) * 2)
|
||||||
|
return self.Color(r,0,255)
|
||||||
|
|
||||||
|
elif p >= 206 and p <= 238:
|
||||||
|
g = (p - 206) * (255/32.0000)
|
||||||
|
return self.Color(0,g,255)
|
||||||
|
|
||||||
|
elif p <= 239 and p <= 250:
|
||||||
|
r = (p - 239) * (255/11.0000)
|
||||||
|
return self.Color(r, 255, 255)
|
||||||
|
|
||||||
|
elif p >= 251 and p <= 270:
|
||||||
|
return self.Color(255, 255, 255)
|
||||||
|
|
||||||
|
elif p >= 271 and p <= 398:
|
||||||
|
rb = 255-((p-271)*2)
|
||||||
|
return self.Color(rb, 255, rb)
|
||||||
|
|
||||||
|
elif p >= 398 and p <= 650:
|
||||||
|
return self.Color(0, 255-(p-398), (p-398))
|
||||||
|
|
||||||
|
else:
|
||||||
|
return self.Color(255, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class Color:
|
||||||
|
red: float
|
||||||
|
green: float
|
||||||
|
blue: float
|
||||||
|
def __init__(self,red:float,green:float,blue:float) -> None:
|
||||||
|
self.red = red
|
||||||
|
self.green = green
|
||||||
|
self.blue = blue
|
@ -1,9 +1,8 @@
|
|||||||
from rgbUtils.BaseEffect import BaseEffect
|
from Utils.BaseEffect import BaseEffect
|
||||||
from rgbUtils.debug import debug
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class offEffect(BaseEffect):
|
class OffEffect(BaseEffect):
|
||||||
name = "offEffect"
|
name = "offEffect"
|
||||||
desc = "LED-Band *sollte* nicht an sein"
|
desc = "LED-Band *sollte* nicht an sein"
|
||||||
|
|
@ -1,9 +1,8 @@
|
|||||||
from rgbUtils.BaseEffect import BaseEffect
|
from Utils.BaseEffect import BaseEffect
|
||||||
from rgbUtils.EffectParameter import slider, colorpicker
|
from Utils.EffectParameter import slider, colorpicker
|
||||||
from rgbUtils.debug import debug
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class onEffect(BaseEffect):
|
class OnEffect(BaseEffect):
|
||||||
name = "onEffect"
|
name = "onEffect"
|
||||||
desc = "LED-Band *sollte* an sein"
|
desc = "LED-Band *sollte* an sein"
|
||||||
|
|
@ -1,8 +1,7 @@
|
|||||||
from rgbUtils.BaseEffect import BaseEffect
|
from Utils.BaseEffect import BaseEffect
|
||||||
from rgbUtils.debug import debug
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class rainbowEffect(BaseEffect):
|
class RainbowEffect(BaseEffect):
|
||||||
name = "rainbowEffect"
|
name = "rainbowEffect"
|
||||||
desc = "LED-Band *sollte* rainbowEffect sein"
|
desc = "LED-Band *sollte* rainbowEffect sein"
|
||||||
|
|
||||||
@ -13,7 +12,6 @@ class rainbowEffect(BaseEffect):
|
|||||||
|
|
||||||
#loop effect as long as not stopped
|
#loop effect as long as not stopped
|
||||||
def effect(self):
|
def effect(self):
|
||||||
debug(self)
|
|
||||||
"""Draw rainbow that fades across all pixels at once."""
|
"""Draw rainbow that fades across all pixels at once."""
|
||||||
for j in range(256*75):
|
for j in range(256*75):
|
||||||
for rgbStrip in self.effectRGBStrips():
|
for rgbStrip in self.effectRGBStrips():
|
@ -1,9 +1,8 @@
|
|||||||
from rgbUtils.BaseEffect import BaseEffect
|
from Utils.BaseEffect import BaseEffect
|
||||||
from rgbUtils.debug import debug
|
|
||||||
from random import randint
|
from random import randint
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class strobeEffect(BaseEffect):
|
class StrobeEffect(BaseEffect):
|
||||||
name = "strobeEffect"
|
name = "strobeEffect"
|
||||||
desc = "*Strobe*"
|
desc = "*Strobe*"
|
||||||
|
|
@ -1,21 +1,26 @@
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
import typing
|
||||||
import copy
|
import copy
|
||||||
from rgbUtils.debug import debug
|
|
||||||
|
|
||||||
class BaseEffect(threading.Thread):
|
class BaseEffect(threading.Thread):
|
||||||
|
|
||||||
# The Name and the Description of the Effect,
|
# The Name and the Description of the Effect,
|
||||||
# should be overwritten by the inheritancing Effect
|
# should be overwritten by the inheritancing Effect
|
||||||
name = "Undefined"
|
name: str = "Undefined"
|
||||||
desc = "No Description"
|
desc: str = "No Description"
|
||||||
|
|
||||||
# Something that will be used to show descriptions and value options
|
# Something that will be used to show descriptions and value options
|
||||||
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
# of the parameters the effect will accept, in a way, that eg the webclient can decide,
|
||||||
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
# if the parameters can be toggeled by a button/checkbox/slider/whatever
|
||||||
effectParameters = []
|
effectParameters: list = []
|
||||||
|
|
||||||
stop = False
|
# musiceffect things
|
||||||
|
# TODO we need a recorderController where the effect binds to and gets its values, this is shit...
|
||||||
|
hasNewFttValues = False
|
||||||
|
ftt: typing.Tuple[typing.List[float],typing.List[float]] = ([],[])
|
||||||
|
|
||||||
|
stop:bool = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
@ -48,7 +53,7 @@ class BaseEffect(threading.Thread):
|
|||||||
# to avoid two effects accessing the rgbStrip
|
# to avoid two effects accessing the rgbStrip
|
||||||
def effect(self):
|
def effect(self):
|
||||||
while 1:
|
while 1:
|
||||||
debug("ET "+self.name+" effect() function needs to be replaced in inheritancing Effect")
|
print("ET "+self.name+" effect() function needs to be replaced in inheritancing Effect")
|
||||||
|
|
||||||
# called when the effect is stopped
|
# called when the effect is stopped
|
||||||
def end(self):
|
def end(self):
|
||||||
@ -75,6 +80,10 @@ class BaseEffect(threading.Thread):
|
|||||||
def onEffectParameterValuesUpdated(self):
|
def onEffectParameterValuesUpdated(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def updateFttValues(self,ftt: typing.Tuple[typing.List[float],typing.List[float]]):
|
||||||
|
self.hasNewFttValues = True
|
||||||
|
self.ftt = ftt
|
||||||
|
|
||||||
# returns a list of the RGBStrips used by this effect
|
# returns a list of the RGBStrips used by this effect
|
||||||
def effectRGBStrips(self):
|
def effectRGBStrips(self):
|
||||||
return self.effectRGBStripList
|
return self.effectRGBStripList
|
245
server/Utils/EffectController.py
Normal file
245
server/Utils/EffectController.py
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# import offEffect
|
||||||
|
|
||||||
|
from Effects.OffEffect import OffEffect
|
||||||
|
from Utils.BaseEffect import BaseEffect
|
||||||
|
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#TypeCheck
|
||||||
|
from typing import Type, List
|
||||||
|
from Utils.RGBStrip import RGBStrip
|
||||||
|
from Utils.RGBStripController import RGBStripController
|
||||||
|
|
||||||
|
|
||||||
|
class EffectController:
|
||||||
|
'''
|
||||||
|
the effectController handles the effects and pushes the values to the rgbStripContoller
|
||||||
|
it also calls the backendProvider's onChange function when there are changes made on the effects:
|
||||||
|
|
||||||
|
- start/stop effects
|
||||||
|
- changes parameters of specific effects
|
||||||
|
- moves the rgbStrips to the effects
|
||||||
|
- runs a guardian that detects dead effectThreads and moves Strips to offEffect
|
||||||
|
'''
|
||||||
|
|
||||||
|
rememberRGBStripEffectThreads: dict = {}
|
||||||
|
|
||||||
|
# list of current running effects
|
||||||
|
effectThreads: List[BaseEffect] = []
|
||||||
|
# the always running base effect
|
||||||
|
offEffectThreadObject: BaseEffect
|
||||||
|
# maybe i will seperate this later
|
||||||
|
onControllerChangeHandler: List[Type[object]] = []
|
||||||
|
#rgbStripController
|
||||||
|
rgbStripController: RGBStripController
|
||||||
|
|
||||||
|
def __init__(self,rgbStripController: RGBStripController) -> None:
|
||||||
|
'''Init the EffectController with the RGBStripController'''
|
||||||
|
self.rgbStripController = rgbStripController
|
||||||
|
# load the effects
|
||||||
|
self.effectsList: List[Type[BaseEffect]] = self.getEffectsListFromDir()
|
||||||
|
# start the offEffect by default
|
||||||
|
self.offEffectThreadObject = self.startEffect(OffEffect,[])
|
||||||
|
|
||||||
|
# - a bit of failover handling, remove dead threads from effectThread array
|
||||||
|
# - move strips without an effect to the offEffect
|
||||||
|
self.effectGuardian = self.EffectGuardian(self)
|
||||||
|
self.effectGuardian.start()
|
||||||
|
|
||||||
|
|
||||||
|
def startEffect(self, effectClass: Type[BaseEffect], rgbStrips: list, params: list = []) -> BaseEffect: #TODO params as a object -> EffectParams
|
||||||
|
'''starts a effect by given class, rgbStrips and params ([[index,[param,param,param]],[index,[param,param,param]]])'''
|
||||||
|
newEffect = effectClass()
|
||||||
|
if len(self.effectThreads) > 0 and len(rgbStrips) > 0 or len(self.effectThreads) == 0:
|
||||||
|
newEffect.start()
|
||||||
|
self.updateEffectParameters(newEffect, params)
|
||||||
|
self.effectThreads.append(newEffect)
|
||||||
|
|
||||||
|
for rgbStrip in rgbStrips:
|
||||||
|
self.moveRGBStripToEffectThread(rgbStrip, newEffect)
|
||||||
|
|
||||||
|
self.noticeControllerChange()
|
||||||
|
return newEffect
|
||||||
|
|
||||||
|
|
||||||
|
def getEffects(self) -> List[Type[BaseEffect]]:
|
||||||
|
'''
|
||||||
|
returns all effectClasses but offEffect, since offEffect will never be killed
|
||||||
|
and should not be running twice
|
||||||
|
'''
|
||||||
|
# alle außer offEffect
|
||||||
|
return self.effectsList
|
||||||
|
|
||||||
|
|
||||||
|
def getEffectThreads(self) -> List[BaseEffect]:
|
||||||
|
'''returns all running effectThreads'''
|
||||||
|
return self.effectThreads
|
||||||
|
|
||||||
|
|
||||||
|
def getEffectRGBStrips(self, effectThreadObject: BaseEffect) -> List[RGBStrip]:
|
||||||
|
'''returns a list of the RGBStrips used by this effect'''
|
||||||
|
return effectThreadObject.effectRGBStrips()
|
||||||
|
|
||||||
|
|
||||||
|
def moveRGBStripToEffectThread(self, rgbStrip: RGBStrip, effectThreadObject: BaseEffect) -> None:
|
||||||
|
'''move a rgbStip to a running effectThread'''
|
||||||
|
# cycle throught all effects and
|
||||||
|
# remove Strip from effect if added
|
||||||
|
for et in self.effectThreads:
|
||||||
|
if rgbStrip in et.effectRGBStrips():
|
||||||
|
et.removeRGBStrip(rgbStrip)
|
||||||
|
if effectThreadObject.isAlive():
|
||||||
|
self.rememberRGBStripEffectThreads[rgbStrip.STRIP_NAME] = et
|
||||||
|
effectThreadObject.addRGBStrip(rgbStrip)
|
||||||
|
|
||||||
|
# check if any effectThread has no more rgbStrips and if so, stop it
|
||||||
|
# if the effectThread has no more strips, we stop it and remove it.
|
||||||
|
for x, effectThread in enumerate(self.effectThreads):
|
||||||
|
# but ignore the first effectthread, since that is our offeffect that should never be killed
|
||||||
|
if len(effectThread.effectRGBStrips()) == 0 and x is not 0:
|
||||||
|
effectThread.stopEffect()
|
||||||
|
self.effectThreads.remove(effectThread)
|
||||||
|
self.noticeControllerChange()
|
||||||
|
|
||||||
|
|
||||||
|
def updateEffectParameters(self, effectThreadObject: BaseEffect, effectParameters: list):
|
||||||
|
'''updates parameter of a running effectThread ([[index,[param,param,param]],[index,[param,param,param]]])'''
|
||||||
|
for effectParameter in effectParameters:
|
||||||
|
effectThreadObject.updateEffectParameterValues(
|
||||||
|
effectParameter[0], effectParameter[1])
|
||||||
|
self.noticeControllerChange()
|
||||||
|
|
||||||
|
|
||||||
|
def stopAll(self):
|
||||||
|
'''stops all effectThreads and set the rgbStrips to off'''
|
||||||
|
print("effectController stopAll()")
|
||||||
|
for effectThread in self.effectThreads:
|
||||||
|
print("effectController killing... "+str(effectThread))
|
||||||
|
effectThread.stopEffect()
|
||||||
|
print("effectController killed "+str(effectThread))
|
||||||
|
|
||||||
|
print("effectController stopping Guardian... ")
|
||||||
|
self.effectGuardian.stop()
|
||||||
|
print("effectController stopped Guardian")
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
def noticeControllerChange(self) -> None:
|
||||||
|
'''inform the controllerChangeHandler to update the client'''
|
||||||
|
for controllerChangeHandler in self.onControllerChangeHandler:
|
||||||
|
controllerChangeHandler()
|
||||||
|
|
||||||
|
def addOnControllerChangeHandler(self, hander: Type[object]) -> None: #TODO class instead of function for OnControllerChangeHandler?
|
||||||
|
'''add onControllerChangeHandler'''
|
||||||
|
print("addOnControllerChangeHandler", str(hander))
|
||||||
|
self.onControllerChangeHandler.append(hander)
|
||||||
|
# send data to this client
|
||||||
|
hander()
|
||||||
|
|
||||||
|
|
||||||
|
def removeOnControllerChangeHandler(self, hander: Type[object]): #TODO class instead of function for OnControllerChangeHandler?
|
||||||
|
'''remove onControllerChangeHandler'''
|
||||||
|
print("removeOnControllerChangeHandler", str(hander))
|
||||||
|
if hander in self.onControllerChangeHandler:
|
||||||
|
self.onControllerChangeHandler.remove(hander)
|
||||||
|
else:
|
||||||
|
print('\n\n -> client was never registered!')
|
||||||
|
|
||||||
|
|
||||||
|
def getEffectsListFromDir(self) -> List[Type[BaseEffect]]:
|
||||||
|
'''automaticly loads all modules from effects subdir and adds them to the list of effects if they have the BaseEffect as subclass'''
|
||||||
|
effectsList: List[Type[BaseEffect]] = []
|
||||||
|
for raw_module in os.listdir(os.path.abspath(os.path.join(os.path.join(os.path.dirname(__file__), os.pardir),"Effects"))):
|
||||||
|
|
||||||
|
if raw_module == '__init__.py' or raw_module == 'OffEffect.py' or raw_module[-3:] != '.py':
|
||||||
|
continue
|
||||||
|
effectModule = __import__("Effects."+raw_module[:-3], fromlist=[raw_module[:-3]])
|
||||||
|
effectClass = getattr(effectModule, raw_module[:-3])
|
||||||
|
if issubclass(effectClass,BaseEffect):
|
||||||
|
effectsList.append(effectClass)
|
||||||
|
print("Loaded effects: ",effectsList)
|
||||||
|
return effectsList
|
||||||
|
|
||||||
|
|
||||||
|
def onRGBStripRegistered(self,rgbStrip: RGBStrip):
|
||||||
|
''' moves new rgbStrip to the offEffectThread and notice all Controller Clients'''
|
||||||
|
if rgbStrip.STRIP_NAME in self.rememberRGBStripEffectThreads:
|
||||||
|
restoredEffect: BaseEffect = self.rememberRGBStripEffectThreads[rgbStrip.STRIP_NAME]
|
||||||
|
if restoredEffect.isAlive():
|
||||||
|
print("stripname in rememberRGBStripEffectThreads -> same effect was never stopped, move to")
|
||||||
|
self.moveRGBStripToEffectThread(rgbStrip,restoredEffect)
|
||||||
|
else:
|
||||||
|
print("stripname in rememberRGBStripEffectThreads -> create new effect")
|
||||||
|
|
||||||
|
effectParamValueList: list = []
|
||||||
|
for x in range(len(restoredEffect.effectParameterValues)):
|
||||||
|
valdict: dict = {}
|
||||||
|
for y in range(len(restoredEffect.effectParameterValues[x])):
|
||||||
|
valdict[y] = restoredEffect.effectParameterValues[x][y]
|
||||||
|
effectParamValueList.append([x,valdict])
|
||||||
|
self.startEffect(restoredEffect.__class__,[rgbStrip],effectParamValueList)
|
||||||
|
self.rememberRGBStripEffectThreads.pop(rgbStrip.STRIP_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("stripname was not in rememberRGBStripEffectThreads")
|
||||||
|
self.offEffectThreadObject.addRGBStrip(rgbStrip)
|
||||||
|
self.noticeControllerChange()
|
||||||
|
|
||||||
|
def onRGBStripUnRegistered(self,rgbStrip: RGBStrip):
|
||||||
|
'''
|
||||||
|
cycle throught all effectThreadss and
|
||||||
|
remove Strip from effect if added
|
||||||
|
'''
|
||||||
|
et: BaseEffect
|
||||||
|
for et in self.effectThreads:
|
||||||
|
if rgbStrip in et.effectRGBStrips():
|
||||||
|
et.removeRGBStrip(rgbStrip)
|
||||||
|
# if the effectThread has no more strips, we stop it and remove it.
|
||||||
|
for x, effectThread in enumerate(self.effectThreads):
|
||||||
|
# but ignore the first effectthread, since that is our offeffect that should never be killed
|
||||||
|
if len(effectThread.effectRGBStrips()) == 0 and x is not 0:
|
||||||
|
effectThread.stopEffect()
|
||||||
|
self.effectThreads.remove(effectThread)
|
||||||
|
self.noticeControllerChange()
|
||||||
|
|
||||||
|
def updateFttValues(self,ftt: tuple):
|
||||||
|
''' TODO this is a themorary funtion util a recorderController is build'''
|
||||||
|
effectThread: BaseEffect
|
||||||
|
for effectThread in self.effectThreads:
|
||||||
|
effectThread.updateFttValues(ftt)
|
||||||
|
|
||||||
|
class EffectGuardian(threading.Thread):
|
||||||
|
'''
|
||||||
|
a bit of failover handling, remove dead threads from effectThread array
|
||||||
|
move strips without an effect to the offEffect
|
||||||
|
|
||||||
|
This Guardian detects Effect Threads that are killed in some unnormal way
|
||||||
|
This is not the procedere how effects are stoped
|
||||||
|
Something went wrong when the guarian is removing dead threads!
|
||||||
|
'''
|
||||||
|
def __init__(self, effectController):
|
||||||
|
threading.Thread.__init__(self, name='EffectGuardian')
|
||||||
|
|
||||||
|
self.effectController = effectController
|
||||||
|
self.stopped = False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while not self.stopped:
|
||||||
|
for effectThread in self.effectController.effectThreads:
|
||||||
|
# if Thread was killed by something else, we remove it from the list
|
||||||
|
if not effectThread.isAlive():
|
||||||
|
for rgbStrip in effectThread.effectRGBStrips():
|
||||||
|
self.effectController.moveRGBStripToEffectThread(rgbStrip,self.effectController.offEffectThreadObject)
|
||||||
|
self.effectController.effectThreads.remove(effectThread)
|
||||||
|
print("effectController:effectGuardian removed dead Thread " +
|
||||||
|
str(effectThread) + ". There must be an error in code!")
|
||||||
|
self.effectController.noticeControllerChange()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.stopped = True
|
@ -1,6 +1,6 @@
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
class effectParameter(object):
|
class EffectParameter(object):
|
||||||
# The Name and the Description of the EffectParameter,
|
# The Name and the Description of the EffectParameter,
|
||||||
# should be overwritten by the inheritancing EffectParameter
|
# should be overwritten by the inheritancing EffectParameter
|
||||||
name="Undefined"
|
name="Undefined"
|
||||||
@ -12,7 +12,7 @@ class effectParameter(object):
|
|||||||
# [min/off,max/on,current,"description"],
|
# [min/off,max/on,current,"description"],
|
||||||
# [min/off,max/on,current,"description"],
|
# [min/off,max/on,current,"description"],
|
||||||
# ]
|
# ]
|
||||||
options = []
|
options: list = []
|
||||||
|
|
||||||
def __init__(self,name,desc,initOptions = []):
|
def __init__(self,name,desc,initOptions = []):
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -27,7 +27,7 @@ class effectParameter(object):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class colorpicker(effectParameter):
|
class colorpicker(EffectParameter):
|
||||||
name="UndefinedColorpicker"
|
name="UndefinedColorpicker"
|
||||||
desc="No Description"
|
desc="No Description"
|
||||||
type="colorpicker"
|
type="colorpicker"
|
||||||
@ -36,7 +36,7 @@ class colorpicker(effectParameter):
|
|||||||
def testValue(self,index,value):
|
def testValue(self,index,value):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class slider(effectParameter):
|
class slider(EffectParameter):
|
||||||
name="UndefinedSlider"
|
name="UndefinedSlider"
|
||||||
desc="No Description"
|
desc="No Description"
|
||||||
type="slider"
|
type="slider"
|
@ -11,14 +11,13 @@
|
|||||||
|
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import scipy
|
|
||||||
import pyaudio
|
import pyaudio
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
class pyAudioRecorder:
|
class PyAudioRecorder:
|
||||||
"""Simple, cross-platform class to record from the default input device."""
|
"""Simple, cross-platform class to record from the default input device."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
@ -1,16 +1,19 @@
|
|||||||
from rgbUtils.debug import debug
|
|
||||||
import uuid
|
import uuid
|
||||||
|
import typing
|
||||||
|
|
||||||
class RGBStrip:
|
class RGBStrip:
|
||||||
# name = the name off the the strip, defined by the client connecting to the server
|
# name = the name off the the strip, defined by the client connecting to the server
|
||||||
# uid = unique id, if the strip sends one, use this (later maybe, or never, whatever)
|
# uid = unique id, if the strip sends one, use this (later maybe, or never, whatever)
|
||||||
# lenght = the lenght off the strip, for future use of eg WS2812b strips, will be 1 by default
|
# lenght = the lenght off the strip, for future use of eg WS2812b strips, will be 1 by default
|
||||||
def __init__(self,name,onValuesUpdateHandler,lenght=1):
|
STRIP_NAME: str
|
||||||
|
STRIP_LENGHT: int
|
||||||
|
def __init__(self,name: str,onValuesUpdateHandler: object,lenght: int=1,address: typing.Tuple[str,int] = ("0.0.0.0",0000)):
|
||||||
# UID should be updateable later, or not?
|
# UID should be updateable later, or not?
|
||||||
# when updating, be sure it does not exist
|
# when updating, be sure it does not exist
|
||||||
self.STRIP_UID = str(uuid.uuid4())
|
self.STRIP_UID = str(uuid.uuid4())
|
||||||
self.STRIP_NAME = name
|
self.STRIP_NAME = name
|
||||||
self.STRIP_LENGHT = lenght
|
self.STRIP_LENGHT = lenght
|
||||||
|
self.address = address
|
||||||
|
|
||||||
self.onValuesUpdateHandler = onValuesUpdateHandler
|
self.onValuesUpdateHandler = onValuesUpdateHandler
|
||||||
|
|
||||||
@ -18,7 +21,7 @@ class RGBStrip:
|
|||||||
self.green = [0]*self.STRIP_LENGHT
|
self.green = [0]*self.STRIP_LENGHT
|
||||||
self.blue = [0]*self.STRIP_LENGHT
|
self.blue = [0]*self.STRIP_LENGHT
|
||||||
|
|
||||||
def RGB(self,red,green,blue,brightness = 100):
|
def RGB(self,red: int,green: int,blue: int,brightness: int = 100):
|
||||||
|
|
||||||
if(red < 0):
|
if(red < 0):
|
||||||
red = 0
|
red = 0
|
||||||
@ -89,7 +92,6 @@ class RGBStrip:
|
|||||||
self.onValuesUpdateHandler(self)
|
self.onValuesUpdateHandler(self)
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
self.hasNewData = False
|
|
||||||
return [self.red,self.green,self.blue]
|
return [self.red,self.green,self.blue]
|
||||||
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
|||||||
from rgbUtils.RGBStrip import RGBStrip
|
from Utils.RGBStrip import RGBStrip
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import json
|
import json
|
||||||
class rgbStripController():
|
class RGBStripController:
|
||||||
|
'''
|
||||||
|
rgbStrips register themselves at the rgbStripContoller
|
||||||
|
the rgbStripController calls the backend Provider's onChange function
|
||||||
|
when there are new values for the strip
|
||||||
|
'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
#threading.Thread.__init__(self)
|
#threading.Thread.__init__(self)
|
||||||
self.rgbStrips = []
|
self.rgbStrips = []
|
||||||
self.onRGBStripRegisteredHandler = []
|
self.onRGBStripRegisteredHandler = []
|
||||||
self.onRGBStripUnRegisteredHandler = []
|
self.onRGBStripUnRegisteredHandler = []
|
||||||
|
|
||||||
def registerRGBStrip(self,rgbStripName,onValuesUpdateHandler,ledcount=1):
|
def registerRGBStrip(self,rgbStripName: str,onValuesUpdateHandler: object,ledcount=1):
|
||||||
# maybe we can use an unique id if the strip reconnects later, eg push the uid
|
# maybe we can use an unique id if the strip reconnects later, eg push the uid
|
||||||
# to the client on first connect and if he reconnects he sould send it back again.
|
# to the client on first connect and if he reconnects he sould send it back again.
|
||||||
# the wmos could use the mac adress, if there is a python script it can save the uid
|
# the wmos could use the mac adress, if there is a python script it can save the uid
|
@ -1,183 +0,0 @@
|
|||||||
# import offEffect
|
|
||||||
from effects.offEffect import offEffect
|
|
||||||
|
|
||||||
from rgbUtils.debug import debug
|
|
||||||
|
|
||||||
from rgbUtils.BaseEffect import BaseEffect
|
|
||||||
from rgbUtils.RGBStrip import RGBStrip
|
|
||||||
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
#
|
|
||||||
# handles all the effects:
|
|
||||||
# - start/stop effects
|
|
||||||
# - changes parameters of specific effects
|
|
||||||
# - moves the rgbStrips to the effects
|
|
||||||
# - runs a guardian that detects dead effectThreads and moves Strips to offEffect
|
|
||||||
#
|
|
||||||
class effectController:
|
|
||||||
|
|
||||||
# list of current running effects
|
|
||||||
effectThreads = []
|
|
||||||
|
|
||||||
offEffectThreadObject = None
|
|
||||||
|
|
||||||
# maybe i will seperate this later
|
|
||||||
onControllerChangeHandler = []
|
|
||||||
|
|
||||||
def __init__(self,rgbStripController):
|
|
||||||
self.rgbStripController = rgbStripController
|
|
||||||
# load the effects
|
|
||||||
self.effectsList = self.getEffectsListFromDir()
|
|
||||||
# start the offEffect by default
|
|
||||||
self.offEffectThreadObject = self.startEffect(offEffect,[])
|
|
||||||
|
|
||||||
# - a bit of failover handling, remove dead threads from effectThread array
|
|
||||||
# - move strips without an effect to the offEffect
|
|
||||||
self.effectGuardian = self.effectGuardian(self)
|
|
||||||
self.effectGuardian.start()
|
|
||||||
|
|
||||||
# starts a effect by given class, rgbStrips and params ([[index,[param,param,param]],[index,[param,param,param]]])
|
|
||||||
def startEffect(self, effectClass: BaseEffect, rgbStrips: list, params: list = []):
|
|
||||||
|
|
||||||
newEffect = effectClass()
|
|
||||||
if len(self.effectThreads) > 0 and len(rgbStrips) > 0 or len(self.effectThreads) == 0:
|
|
||||||
newEffect.start()
|
|
||||||
self.updateEffectParameters(newEffect, params)
|
|
||||||
self.effectThreads.append(newEffect)
|
|
||||||
|
|
||||||
for rgbStrip in rgbStrips:
|
|
||||||
self.moveRGBStripToEffectThread(rgbStrip, newEffect)
|
|
||||||
|
|
||||||
self.noticeControllerChange()
|
|
||||||
return newEffect
|
|
||||||
|
|
||||||
# returns all effectClasses but offEffect, since offEffect will never be killed
|
|
||||||
# and should not be running twice
|
|
||||||
def getEffects(self):
|
|
||||||
# alle außer offEffect
|
|
||||||
return self.effectsList
|
|
||||||
|
|
||||||
# returns all running effectThreads
|
|
||||||
def getEffectThreads(self):
|
|
||||||
return self.effectThreads
|
|
||||||
|
|
||||||
# returns a list of the RGBStrips used by this effect
|
|
||||||
def getEffectRGBStrips(self, effectThreadObject: BaseEffect):
|
|
||||||
return effectThreadObject.effectRGBStrips()
|
|
||||||
|
|
||||||
# move a rgbStip to a running effectThread
|
|
||||||
def moveRGBStripToEffectThread(self, rgbStrip: RGBStrip, effectThreadObject: BaseEffect):
|
|
||||||
# cycle throught all effects and
|
|
||||||
# remove Strip from effect if added
|
|
||||||
for et in self.effectThreads:
|
|
||||||
if rgbStrip in et.effectRGBStrips():
|
|
||||||
et.removeRGBStrip(rgbStrip)
|
|
||||||
if effectThreadObject.isAlive():
|
|
||||||
effectThreadObject.addRGBStrip(rgbStrip)
|
|
||||||
# check if any effectThread has no more rgbStrips and if so, stop it
|
|
||||||
|
|
||||||
# if the effectThread has no more strips, we stop it and remove it.
|
|
||||||
for x, effectThread in enumerate(self.effectThreads):
|
|
||||||
if len(effectThread.effectRGBStrips()) == 0 and x is not 0:
|
|
||||||
effectThread.stopEffect()
|
|
||||||
self.effectThreads.remove(effectThread)
|
|
||||||
self.noticeControllerChange()
|
|
||||||
|
|
||||||
# updates parameter of a running effectThread
|
|
||||||
def updateEffectParameters(self, effectThreadObject: BaseEffect, effectParameters):
|
|
||||||
for effectParameter in effectParameters:
|
|
||||||
effectThreadObject.updateEffectParameterValues(
|
|
||||||
effectParameter[0], effectParameter[1])
|
|
||||||
self.noticeControllerChange()
|
|
||||||
|
|
||||||
# stops all effectThreads and set the rgbStrips to off
|
|
||||||
def stopAll(self):
|
|
||||||
debug("effectController stopAll()")
|
|
||||||
for effectThread in self.effectThreads:
|
|
||||||
effectThread.stopEffect()
|
|
||||||
debug("effectController killed "+str(effectThread))
|
|
||||||
|
|
||||||
self.effectGuardian.stop()
|
|
||||||
|
|
||||||
time.sleep(0.5)
|
|
||||||
# GPIO.cleanup()
|
|
||||||
|
|
||||||
# inform the controllerChangeHandler to update the client
|
|
||||||
def noticeControllerChange(self):
|
|
||||||
for controllerChangeHandler in self.onControllerChangeHandler:
|
|
||||||
controllerChangeHandler()
|
|
||||||
|
|
||||||
# add onControllerChangeHandler
|
|
||||||
def addOnControllerChangeHandler(self, hander):
|
|
||||||
print("addOnControllerChangeHandler", str(hander))
|
|
||||||
self.onControllerChangeHandler.append(hander)
|
|
||||||
# send data to this client
|
|
||||||
hander()
|
|
||||||
|
|
||||||
# remove onControllerChangeHandler
|
|
||||||
def removeOnControllerChangeHandler(self, hander):
|
|
||||||
print("removeOnControllerChangeHandler", str(hander))
|
|
||||||
if hander in self.onControllerChangeHandler:
|
|
||||||
self.onControllerChangeHandler.remove(hander)
|
|
||||||
else:
|
|
||||||
print('\n\n -> client was never registered!')
|
|
||||||
|
|
||||||
# automaticly loads all modules from effects subdir and adds them to the list of effects if they have the BaseEffect as subclass
|
|
||||||
def getEffectsListFromDir(self):
|
|
||||||
effectsList = []
|
|
||||||
for raw_module in os.listdir(os.path.abspath(os.path.join(os.path.join(os.path.dirname(__file__), os.pardir),"effects"))):
|
|
||||||
|
|
||||||
if raw_module == '__init__.py' or raw_module == 'offEffect.py' or raw_module[-3:] != '.py':
|
|
||||||
continue
|
|
||||||
effectModule = __import__("effects."+raw_module[:-3], fromlist=[raw_module[:-3]])
|
|
||||||
effectClass = getattr(effectModule, raw_module[:-3])
|
|
||||||
if issubclass(effectClass,BaseEffect):
|
|
||||||
effectsList.append(effectClass)
|
|
||||||
print("Loaded effects: ",effectsList)
|
|
||||||
return effectsList
|
|
||||||
|
|
||||||
def onRGBStripRegistered(self,rgbStrip):
|
|
||||||
self.offEffectThreadObject.addRGBStrip(rgbStrip)
|
|
||||||
self.noticeControllerChange()
|
|
||||||
|
|
||||||
def onRGBStripUnRegistered(self,rgbStrip):
|
|
||||||
# cycle throught all effects and
|
|
||||||
# remove Strip from effect if added
|
|
||||||
for et in self.effectThreads:
|
|
||||||
if rgbStrip in et.effectRGBStrips():
|
|
||||||
et.removeRGBStrip(rgbStrip)
|
|
||||||
# if the effectThread has no more strips, we stop it and remove it.
|
|
||||||
for x, effectThread in enumerate(self.effectThreads):
|
|
||||||
if len(effectThread.effectRGBStrips()) == 0 and x is not 0:
|
|
||||||
effectThread.stopEffect()
|
|
||||||
self.effectThreads.remove(effectThread)
|
|
||||||
self.noticeControllerChange()
|
|
||||||
|
|
||||||
# - a bit of failover handling, remove dead threads from effectThread array
|
|
||||||
# - move strips without an effect to the offEffect
|
|
||||||
class effectGuardian(threading.Thread):
|
|
||||||
def __init__(self, effectController):
|
|
||||||
threading.Thread.__init__(self, name='effectGuardian')
|
|
||||||
|
|
||||||
self.effectController = effectController
|
|
||||||
self.stopped = False
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while not self.stopped:
|
|
||||||
for effectThread in self.effectController.effectThreads:
|
|
||||||
# if Thread was killed by something else, we remove it from the list
|
|
||||||
if not effectThread.isAlive():
|
|
||||||
for rgbStrip in effectThread.effectRGBStrips():
|
|
||||||
self.effectController.moveRGBStripToEffectThread(rgbStrip,self.effectController.offEffectThreadObject)
|
|
||||||
self.effectController.effectThreads.remove(effectThread)
|
|
||||||
print("effectController:effectGuardian removed dead Thread " +
|
|
||||||
str(effectThread) + ". There must be an error in code!")
|
|
||||||
self.effectController.noticeControllerChange()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.stopped = True
|
|
Loading…
x
Reference in New Issue
Block a user