reorganize
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import time
|
||||
import threading
|
||||
import copy
|
||||
from rgbUtils.debug import debug
|
||||
|
||||
class BaseEffect(threading.Thread):
|
||||
|
||||
# The Name and the Description of the Effect,
|
||||
# should be overwritten by the inheritancing Effect
|
||||
name = "Undefined"
|
||||
desc = "No Description"
|
||||
|
||||
# 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
|
||||
effectParameters = []
|
||||
|
||||
stop = False
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.effectRGBStripList = []
|
||||
|
||||
# when a strip is added or removed or the options change, the thread will not restart,
|
||||
# the changes are pushed to the thread at livetime. so avoid log running loops without
|
||||
# accessing getRGBStrips and getEffectParams. It could happen, that a new effect for example already uses
|
||||
# a rgbStip while the loop of the old effect is running and has not got the changes.
|
||||
def run(self):
|
||||
self.effectParameterValues = []
|
||||
for effectParameterIndex, effectParameter in enumerate(self.effectParameters):
|
||||
self.effectParameterValues.append([])
|
||||
for option in effectParameter.options:
|
||||
self.effectParameterValues[effectParameterIndex].append(option[2])
|
||||
|
||||
print(self.effectParameterValues)
|
||||
self.init()
|
||||
while not self.stop:
|
||||
# run the effect in endless while
|
||||
self.effect()
|
||||
self.end()
|
||||
|
||||
# Init is called bevor the loop, use setEffectParams for init values
|
||||
# and access them in the effect with getEffectParams
|
||||
def init(self):
|
||||
return
|
||||
|
||||
# see run(): never save effectRGBStrips() as a variable and access it as often as possible
|
||||
# to avoid two effects accessing the rgbStrip
|
||||
def effect(self):
|
||||
while 1:
|
||||
debug("ET "+self.name+" effect() function needs to be replaced in inheritancing Effect")
|
||||
|
||||
# called when the effect is stopped
|
||||
def end(self):
|
||||
return
|
||||
|
||||
# when called the effect loop will stop and the thread can be terminated
|
||||
def stopEffect(self):
|
||||
self.stop = True
|
||||
|
||||
# the effect itself will know its own strips. i don't know if it would be better if the effectController self
|
||||
# should have this list, but then i must do something like nested arrays, naah.
|
||||
def addRGBStrip(self,rgbStrip):
|
||||
self.effectRGBStripList.append(rgbStrip)
|
||||
self.onRGBStripAdded(rgbStrip)
|
||||
|
||||
# remove a strip, if the effect has no more strips, the effect thread guardian will kill it
|
||||
def removeRGBStrip(self,rgbStrip):
|
||||
self.effectRGBStripList.remove(rgbStrip)
|
||||
|
||||
# for overriding by the effect, when a strip is added
|
||||
def onRGBStripAdded(self,rgbStrip):
|
||||
return
|
||||
# for overriding by the effect, when a strip is added
|
||||
def onEffectParameterValuesUpdated(self):
|
||||
return
|
||||
|
||||
# returns a list of the RGBStrips used by this effect
|
||||
def effectRGBStrips(self):
|
||||
return self.effectRGBStripList
|
||||
|
||||
def addEffectParameter(self,effectParameter):
|
||||
self.effectParameters.append(effectParameter)
|
||||
|
||||
# set Params as descriped in getParamsDescription()
|
||||
def updateEffectParameterValues(self,effectParameterIndex, effectParameterValues):
|
||||
print("updateEffectParameterValues",effectParameterIndex,effectParameterValues)
|
||||
for effectParameterValue in effectParameterValues:
|
||||
if self.effectParameters[int(effectParameterIndex)].testValue(int(effectParameterValue),int(effectParameterValues[effectParameterValue])):
|
||||
self.effectParameterValues[int(effectParameterIndex)][int(effectParameterValue)] = int(effectParameterValues[effectParameterValue])
|
||||
self.onEffectParameterValuesUpdated()
|
||||
@@ -0,0 +1,42 @@
|
||||
import threading
|
||||
|
||||
class effectParameter(object):
|
||||
# The Name and the Description of the EffectParameter,
|
||||
# should be overwritten by the inheritancing EffectParameter
|
||||
name="Undefined"
|
||||
desc = "No Description"
|
||||
|
||||
# In the order you expect the options to be set
|
||||
# [
|
||||
# [min/off,max/on,current,"description"],
|
||||
# [min/off,max/on,current,"description"],
|
||||
# [min/off,max/on,current,"description"],
|
||||
# ]
|
||||
options = []
|
||||
|
||||
def __init__(self,name,desc,initOptions = []):
|
||||
self.name = name
|
||||
self.desc = desc
|
||||
self.options = initOptions
|
||||
|
||||
# check if the given values are plausible
|
||||
def testValue(self,index,value):
|
||||
if value >= self.options[index][0] \
|
||||
and value <= self.options[index][1]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class colorpicker(effectParameter):
|
||||
name="UndefinedColorpicker"
|
||||
desc="No Description"
|
||||
type="colorpicker"
|
||||
|
||||
# check if the given values are plausible
|
||||
def testValue(self,index,value):
|
||||
return True
|
||||
|
||||
class slider(effectParameter):
|
||||
name="UndefinedSlider"
|
||||
desc="No Description"
|
||||
type="slider"
|
||||
@@ -0,0 +1,98 @@
|
||||
try:
|
||||
import RPi.GPIO as GPIO
|
||||
except ImportError:
|
||||
import rgbUtils.fakeGPIO as GPIO
|
||||
from rgbUtils.debug import debug
|
||||
class RGBStrip:
|
||||
STRIP_NAME = None
|
||||
|
||||
issetup = False
|
||||
|
||||
effectClassInstance = None
|
||||
|
||||
# GPIO PWM Frequenz
|
||||
hertz = 100
|
||||
|
||||
RED_PIN = None
|
||||
GREEN_PIN = None
|
||||
BLUE_PIN = None
|
||||
|
||||
RED_PWM = None
|
||||
GREEN_PWM = None
|
||||
BLUE_PWM = None
|
||||
|
||||
|
||||
def __init__(self,STRIP_NAME,RED_PIN,GREEN_PIN,BLUE_PIN):
|
||||
self.STRIP_NAME = STRIP_NAME
|
||||
self.RED_PIN = RED_PIN
|
||||
self.GREEN_PIN = GREEN_PIN
|
||||
self.BLUE_PIN = BLUE_PIN
|
||||
|
||||
def init(self):
|
||||
if not self.issetup:
|
||||
debug("setting up")
|
||||
# setup RED
|
||||
GPIO.setup(self.RED_PIN, GPIO.OUT)
|
||||
self.RED_PWM = GPIO.PWM(self.RED_PIN, self.hertz)
|
||||
self.RED_PWM.start(0)
|
||||
# setup GREEN
|
||||
GPIO.setup(self.GREEN_PIN, GPIO.OUT)
|
||||
self.GREEN_PWM = GPIO.PWM(self.GREEN_PIN, self.hertz)
|
||||
self.GREEN_PWM.start(0)
|
||||
# setup BLUE
|
||||
GPIO.setup(self.BLUE_PIN, GPIO.OUT)
|
||||
self.BLUE_PWM = GPIO.PWM(self.BLUE_PIN, self.hertz)
|
||||
self.BLUE_PWM.start(0)
|
||||
|
||||
self.issetup = True
|
||||
|
||||
def RGB(self,red,green,blue,brightness = 100):
|
||||
|
||||
if(red < 0):
|
||||
red = 0
|
||||
if(red > 255):
|
||||
red = 255
|
||||
|
||||
if(green < 0):
|
||||
green = 0
|
||||
if(green > 255):
|
||||
green = 255
|
||||
|
||||
if(blue < 0):
|
||||
blue = 0
|
||||
if(blue > 255):
|
||||
blue = 255
|
||||
|
||||
if(brightness < 0):
|
||||
brightness = 0
|
||||
if(brightness > 100):
|
||||
brightness = 100
|
||||
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
self.RED_PWM.ChangeDutyCycle(round((red/255*100)/100*brightness,2))
|
||||
self.GREEN_PWM.ChangeDutyCycle(round((green/255*100)/100*brightness,2))
|
||||
self.BLUE_PWM.ChangeDutyCycle(round((blue/255*100)/100*brightness,2))
|
||||
|
||||
def stop(self):
|
||||
self.RED_PWM.ChangeDutyCycle(0)
|
||||
self.GREEN_PWM.ChangeDutyCycle(0)
|
||||
self.BLUE_PWM.ChangeDutyCycle(0)
|
||||
self.RED_PWM.stop()
|
||||
self.GREEN_PWM.stop()
|
||||
self.BLUE_PWM.stop()
|
||||
|
||||
|
||||
|
||||
# I have to be sure that a rgbStip is only used by one Effect at once, so i save the current running Effect Class Object
|
||||
# in the rgbStip object.
|
||||
def setEffectClassInstance(self,effectClass):
|
||||
debug("RGBStrip "+ self.STRIP_NAME + " Effect is now " + str(effectClass))
|
||||
self.effectClassInstance = effectClass
|
||||
|
||||
def getEffectClassInstance(self):
|
||||
return self.effectClassInstance
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
from rgbUtils.debug import debug
|
||||
import uuid
|
||||
|
||||
class RGBStrip:
|
||||
# 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)
|
||||
# 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):
|
||||
# UID should be updateable later, or not?
|
||||
# when updating, be sure it does not exist
|
||||
self.STRIP_UID = str(uuid.uuid4())
|
||||
self.STRIP_NAME = name
|
||||
self.STRIP_LEGHT = lenght
|
||||
|
||||
self.onValuesUpdateHandler = onValuesUpdateHandler
|
||||
|
||||
self.red = [0]*self.STRIP_LEGHT
|
||||
self.green = [0]*self.STRIP_LEGHT
|
||||
self.blue = [0]*self.STRIP_LEGHT
|
||||
|
||||
def RGB(self,red,green,blue,brightness = 100):
|
||||
|
||||
if(red < 0):
|
||||
red = 0
|
||||
if(red > 255):
|
||||
red = 255
|
||||
|
||||
if(green < 0):
|
||||
green = 0
|
||||
if(green > 255):
|
||||
green = 255
|
||||
|
||||
if(blue < 0):
|
||||
blue = 0
|
||||
if(blue > 255):
|
||||
blue = 255
|
||||
|
||||
if(brightness < 0):
|
||||
brightness = 0
|
||||
if(brightness > 100):
|
||||
brightness = 100
|
||||
|
||||
for x in range(self.STRIP_LEGHT):
|
||||
self.red[x] = int(red/100*brightness)
|
||||
self.green[x] = int(green/100*brightness)
|
||||
self.blue[x] = int(blue/100*brightness)
|
||||
|
||||
self.onValuesUpdateHandler(self)
|
||||
|
||||
def WS2812b(self,id,red,green,blue,brightness=100):
|
||||
if id < 0 and id > self.STRIP_LEGHT:
|
||||
print(self.STRIP_NAME," is max ",self.STRIP_LEGHT," Pixels long!")
|
||||
return
|
||||
else:
|
||||
if(red < 0):
|
||||
red = 0
|
||||
if(red > 255):
|
||||
red = 255
|
||||
|
||||
if(green < 0):
|
||||
green = 0
|
||||
if(green > 255):
|
||||
green = 255
|
||||
|
||||
if(blue < 0):
|
||||
blue = 0
|
||||
if(blue > 255):
|
||||
blue = 255
|
||||
|
||||
if(brightness < 0):
|
||||
brightness = 0
|
||||
if(brightness > 100):
|
||||
brightness = 100
|
||||
|
||||
self.red[id] = int(red/100*brightness)
|
||||
self.green[id] = int(green/100*brightness)
|
||||
self.blue[id] = int(blue/100*brightness)
|
||||
|
||||
self.onValuesUpdateHandler(self)
|
||||
|
||||
|
||||
def off(self):
|
||||
for x in range(self.STRIP_LEGHT):
|
||||
self.red[x] = 0
|
||||
self.green[x] = 0
|
||||
self.blue[x] = 0
|
||||
|
||||
self.onValuesUpdateHandler(self)
|
||||
|
||||
def getData(self):
|
||||
self.hasNewData = False
|
||||
return [self.red,self.green,self.blue]
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# The Master Strip that can be used as multiple RGBStrips or later as one for Special Effects
|
||||
# see https://github.com/mdaffin/PyNeoPixel for arduino ino
|
||||
|
||||
import struct
|
||||
import serial
|
||||
import time
|
||||
|
||||
|
||||
class WS2812MasterStrip:
|
||||
|
||||
def __init__(self,port,LED_COUNT):
|
||||
|
||||
#serial connection
|
||||
self.port = port
|
||||
self.ser = serial.Serial(self.port, 115200, timeout=1)
|
||||
self.command_count = 0
|
||||
|
||||
self.LED_COUNT = LED_COUNT
|
||||
|
||||
self.lock = False
|
||||
|
||||
self.setPixelColorRange(1,LED_COUNT, 0, 0, 0)
|
||||
|
||||
def setPixelColorRange(self, pixelstart, pixelstop, red, green, blue):
|
||||
# locking while sending serial data, because multiple threads
|
||||
# could try to send data at the same time
|
||||
while self.lock:
|
||||
#time.sleep(0.001)
|
||||
return
|
||||
self.lock = True
|
||||
|
||||
for pixel in [pixelstart,pixelstop]:
|
||||
if pixelstart > self.LED_COUNT:
|
||||
print("WS2812MasterStrip@"+self.port+" Strip is only "+self.LED_COUNT+" pixels long")
|
||||
return
|
||||
pixelstart = pixelstart - 1
|
||||
pixelstop = pixelstop - 1
|
||||
message = struct.pack('>BBBHHBBB', ord(':'), self.command_count, ord('r'), pixelstart, pixelstop, red, green, blue)
|
||||
self.command_count += 1
|
||||
if self.command_count >=255:
|
||||
self.command_count = 0
|
||||
#print(message)
|
||||
self.ser.write(message)
|
||||
response = self.ser.readline()
|
||||
self.lock = False
|
||||
#print(response)
|
||||
@@ -0,0 +1,61 @@
|
||||
from rgbUtils.debug import debug
|
||||
from rgbUtils.RGBStrip import RGBStrip
|
||||
|
||||
# The WS2812Strip must have the same behavior as the 12v rgb strip,
|
||||
# plus some extras for these type of strips only.
|
||||
# A WS1812 Strip can be initialised as multiple RGB Strips by defining
|
||||
# the start and the end led's. lets's see how that goes
|
||||
class WS2812Strip(RGBStrip):
|
||||
STRIP_NAME = None
|
||||
|
||||
master = None
|
||||
FIRST_LED = None
|
||||
LAST_LED = None
|
||||
|
||||
def __init__(self,STRIP_NAME,FIRST_LED,LAST_LED,WS2812B_MASTER_STRIP):
|
||||
self.STRIP_NAME = STRIP_NAME
|
||||
self.master = WS2812B_MASTER_STRIP
|
||||
self.FIRST_LED = FIRST_LED
|
||||
self.LAST_LED = LAST_LED
|
||||
|
||||
# init the WS2812 part. since the master sets all pixel to 0 in init,
|
||||
# we have nothing to do here.
|
||||
def init(self):
|
||||
if not self.issetup:
|
||||
debug("setting up")
|
||||
self.issetup = True
|
||||
|
||||
def RGB(self,red,green,blue,brightness = 100):
|
||||
|
||||
if(red < 0):
|
||||
red = 0
|
||||
if(red > 255):
|
||||
red = 255
|
||||
|
||||
if(green < 0):
|
||||
green = 0
|
||||
if(green > 255):
|
||||
green = 255
|
||||
|
||||
if(blue < 0):
|
||||
blue = 0
|
||||
if(blue > 255):
|
||||
blue = 255
|
||||
green = 255
|
||||
|
||||
if(brightness < 0):
|
||||
brightness = 0
|
||||
if(brightness > 255):
|
||||
brightness = 100
|
||||
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
self.master.setPixelColorRange(self.FIRST_LED,self.LAST_LED, int(round(red/100*brightness)), int(round(green/100*brightness)), int(round(blue/100*brightness)))
|
||||
|
||||
|
||||
def stop(self):
|
||||
self.master.setPixelColorRange(self.FIRST_LED,self.LAST_LED, 0, 0,0)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
def debug(string):
|
||||
return
|
||||
print(string)
|
||||
@@ -0,0 +1,174 @@
|
||||
# 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()
|
||||
newEffect.start()
|
||||
self.updateEffectParameters(newEffect, params)
|
||||
self.effectThreads.append(newEffect)
|
||||
|
||||
for rgbStrip in rgbStrips:
|
||||
self.moveRGBStripToEffectThread(rgbStrip, newEffect)
|
||||
|
||||
# if the effectThread has no strips, we stop it and remove it.
|
||||
if len(newEffect.effectRGBStrips()) == 0:
|
||||
newEffect.stopEffect()
|
||||
self.effectThreads.remove(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))
|
||||
self.onControllerChangeHandler.remove(hander)
|
||||
|
||||
# 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)
|
||||
self.noticeControllerChange()
|
||||
|
||||
class effectGuardian(threading.Thread):
|
||||
def __init__(self, effectController):
|
||||
threading.Thread.__init__(self)
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Convert the effectController function outputs to json format
|
||||
"""
|
||||
|
||||
def responseHandler(effectController,rgbStripController,data):
|
||||
if "startEffect" in data:
|
||||
enabledRGBStrips = []
|
||||
for rgbStrip in rgbStripController.getRGBStrips():
|
||||
for rgbStripJsonArray in data['startEffect']['rgbStrips']:
|
||||
if rgbStrip.STRIP_UID in rgbStripJsonArray[0] and rgbStripJsonArray[1]:
|
||||
enabledRGBStrips.append(rgbStrip)
|
||||
|
||||
effectController.startEffect(effectController.getEffects()[data['startEffect']['effect']],enabledRGBStrips,data['startEffect']['params'])
|
||||
if "moveRGBStripToEffectThread" in data:
|
||||
for rgbStrip in rgbStripController.getRGBStrips():
|
||||
if rgbStrip.STRIP_UID in data['moveRGBStripToEffectThread']['rgbStrip']:
|
||||
effectController.moveRGBStripToEffectThread( \
|
||||
rgbStrip, \
|
||||
effectController.getEffectThreads()[data['moveRGBStripToEffectThread']['effectThread']] \
|
||||
)
|
||||
if "effectThreadChangeEffectParam" in data:
|
||||
effectController.updateEffectParameters(\
|
||||
effectController.getEffectThreads()[data['effectThreadChangeEffectParam']['effectThread']], \
|
||||
data['effectThreadChangeEffectParam']['params']
|
||||
)
|
||||
return 'ok'
|
||||
|
||||
# return json of all configured effects (except offEffect) with their paramDescriptions
|
||||
def getEffects(effectController):
|
||||
result = {}
|
||||
for x, effect in enumerate(effectController.getEffects()):
|
||||
effectParams = {}
|
||||
for y, effectParam in enumerate(effect.effectParameters):
|
||||
effectParams[y] = {'index': y,'type': effectParam.type, 'name': effectParam.name, 'desc': effectParam.desc, 'options': effectParam.options}
|
||||
result[x] = {'index': x, 'name': effect.name, 'desc': effect.desc, 'effectParams': effectParams}
|
||||
return result
|
||||
|
||||
# return json of all running effectThreads with their active rgbStrips and params
|
||||
def getEffectThreads(effectController):
|
||||
result = {}
|
||||
for x, effectThread in enumerate(effectController.getEffectThreads()):
|
||||
effectRGBStrips = {}
|
||||
for effectRGBStrip in effectController.getEffectRGBStrips(effectThread):
|
||||
effectRGBStrips[effectRGBStrip.STRIP_UID] = {'index': effectRGBStrip.STRIP_UID, 'name': effectRGBStrip.STRIP_NAME}
|
||||
effectParams = {}
|
||||
for z, effectParam in enumerate(effectThread.effectParameters):
|
||||
effectParams[z] = {'index': z,'type': effectParam.type, 'name': effectParam.name, 'desc': effectParam.desc, 'options': effectParam.options, 'values': effectThread.effectParameterValues[z]}
|
||||
result[x] = {'index': x, 'name': effectThread.name, 'desc': effectThread.desc,'activeRGBStips': effectRGBStrips, 'dump': str(effectThread), 'effectParams': effectParams}
|
||||
return result
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Fake RPi.GPIO package
|
||||
'''
|
||||
#______________________________________________________________________
|
||||
# imports
|
||||
|
||||
#______________________________________________________________________
|
||||
# globals
|
||||
BCM = 'BCM'
|
||||
BOARD = 'BOARD'
|
||||
|
||||
OUT = 'OUT'
|
||||
IN = 'IN'
|
||||
|
||||
LOW = 'LOW'
|
||||
HIGH = 'HIGH'
|
||||
|
||||
FALLING = 'FALLING'
|
||||
RISING = 'RISING'
|
||||
|
||||
PUD_UP = 'PUD_UP'
|
||||
PUD_DOWN = 'PUD_DOWN'
|
||||
|
||||
RPI_INFO = {'INFO': 'Fake GPIO, ¬¬', 'P1_REVISION': 'Fake GPIO, ¬¬'}
|
||||
|
||||
VERSION = '0.2.0a0'
|
||||
|
||||
#______________________________________________________________________
|
||||
# VERBOSE
|
||||
VERBOSE = False
|
||||
|
||||
#______________________________________________________________________
|
||||
# functions wrapper/ base fucntion
|
||||
def print_data(func):
|
||||
def verbose(*args, **kwargs):
|
||||
if VERBOSE:
|
||||
print(func.__name__, args, kwargs)
|
||||
return verbose
|
||||
|
||||
#______________________________________________________________________
|
||||
# PWM class
|
||||
class PWM(object):
|
||||
|
||||
@print_data
|
||||
def __init__(self, channel, frequency):
|
||||
self.channel = channel
|
||||
self.frequency = frequency
|
||||
self.dc = 0
|
||||
|
||||
@print_data
|
||||
def start(self, dc):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def ChangeFrequency(self, freq):
|
||||
self.frequency = freq
|
||||
|
||||
@print_data
|
||||
def ChangeDutyCycle(self, dc):
|
||||
self.dc = dc
|
||||
|
||||
@print_data
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
#______________________________________________________________________
|
||||
# functions
|
||||
|
||||
@print_data
|
||||
def setmode(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def setup(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def output(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def input(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def cleanup(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def wait_for_edge(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def event_detected(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def add_event_detect(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def add_event_callback(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def remove_event_detect(*args, **kwargs):
|
||||
pass
|
||||
|
||||
@print_data
|
||||
def setwarnings(*args, **kwargs):
|
||||
pass
|
||||
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# The idea is to have only one thread accessing the audio input source instead
|
||||
# of every music enabled thread itself. also, different fuctions calculating
|
||||
# frequencys and so on shoud run in own threads to the leds get more updates
|
||||
#
|
||||
# in history one thread was calculating bpm, freqence average and max values in one thread
|
||||
# before updating the leds, what the pi was a bit slow for and you cloud count every update
|
||||
# of the leds
|
||||
|
||||
# i think most of it is from https://github.com/shunfu/python-beat-detector/
|
||||
|
||||
|
||||
import numpy
|
||||
import scipy
|
||||
import pyaudio
|
||||
import threading
|
||||
|
||||
import time
|
||||
|
||||
|
||||
class pyAudioRecorder:
|
||||
"""Simple, cross-platform class to record from the default input device."""
|
||||
|
||||
def __init__(self):
|
||||
self.RATE = 44100
|
||||
self.BUFFERSIZE = 2**12
|
||||
self.secToRecord = .1
|
||||
self.kill_threads = False
|
||||
self.has_new_audio = False
|
||||
self.setup()
|
||||
|
||||
# since the server only can handly one input (for now) this thread will calculate
|
||||
# some basic things for the musicEffectsThreads, so they can update the leds more often.
|
||||
# it is always posible to catch the fft() function and do your own thing in your musikEffect.
|
||||
|
||||
# calculate bpm, this is the same for all clientThreads
|
||||
self.beats_idx = 0
|
||||
|
||||
self.bpm_list = []
|
||||
self.prev_beat = time.perf_counter()
|
||||
self.low_freq_avg_list = []
|
||||
|
||||
self.lastTime = time.time()
|
||||
|
||||
|
||||
self.recorderClients = []
|
||||
|
||||
def setup(self):
|
||||
self.buffers_to_record = int(
|
||||
self.RATE * self.secToRecord / self.BUFFERSIZE)
|
||||
if self.buffers_to_record == 0:
|
||||
self.buffers_to_record = 1
|
||||
self.samples_to_record = int(self.BUFFERSIZE * self.buffers_to_record)
|
||||
self.chunks_to_record = int(self.samples_to_record / self.BUFFERSIZE)
|
||||
self.sec_per_point = 1. / self.RATE
|
||||
|
||||
self.p = pyaudio.PyAudio()
|
||||
# start the PyAudio class
|
||||
for i in range(self.p.get_device_count()):
|
||||
devinfo = self.p.get_device_info_by_index(i)
|
||||
print(i, devinfo["name"])
|
||||
# make sure the default input device is broadcasting the speaker output
|
||||
# there are a few ways to do this
|
||||
# e.g., stereo mix, VB audio cable for windows, soundflower for mac
|
||||
self.in_stream = self.p.open(format=pyaudio.paInt16,
|
||||
channels=1,
|
||||
rate=self.RATE,
|
||||
input=True,
|
||||
frames_per_buffer=self.BUFFERSIZE)
|
||||
print("Using default input device: {:s}".format(
|
||||
self.p.get_default_input_device_info()['name']))
|
||||
|
||||
self.audio = numpy.empty(
|
||||
(self.chunks_to_record * self.BUFFERSIZE), dtype=numpy.int16)
|
||||
|
||||
def close(self):
|
||||
print("pyAudioRecorder closed")
|
||||
self.kill_threads = True
|
||||
self.p.close(self.in_stream)
|
||||
|
||||
### RECORDING AUDIO ###
|
||||
|
||||
def get_audio(self):
|
||||
"""get a single buffer size worth of audio."""
|
||||
audio_string = self.in_stream.read(self.BUFFERSIZE)
|
||||
return numpy.fromstring(audio_string, dtype=numpy.int16)
|
||||
|
||||
def record(self):
|
||||
while not self.kill_threads:
|
||||
for i in range(self.chunks_to_record):
|
||||
self.audio[i*self.BUFFERSIZE:(i+1)
|
||||
* self.BUFFERSIZE] = self.get_audio()
|
||||
self.has_new_audio = True
|
||||
|
||||
def start(self):
|
||||
print("pyAudioRecorder started")
|
||||
self.t = threading.Thread(target=self.record)
|
||||
self.t.start()
|
||||
|
||||
### MATH ###
|
||||
|
||||
def downsample(self, data, mult):
|
||||
"""Given 1D data, return the binned average."""
|
||||
overhang = len(data) % mult
|
||||
if overhang:
|
||||
data = data[:-overhang]
|
||||
data = numpy.reshape(data, (len(data) / mult, mult))
|
||||
data = numpy.average(data, 1)
|
||||
return data
|
||||
|
||||
def fft(self, data=None, trim_by=10, log_scale=False, div_by=100):
|
||||
if not data:
|
||||
data = self.audio.flatten()
|
||||
left, right = numpy.split(numpy.abs(numpy.fft.fft(data)), 2)
|
||||
ys = numpy.add(left, right[::-1])
|
||||
if log_scale:
|
||||
ys = numpy.multiply(20, numpy.log10(ys))
|
||||
xs = numpy.arange(self.BUFFERSIZE/2, dtype=float)
|
||||
if trim_by:
|
||||
i = int((self.BUFFERSIZE/2) / trim_by)
|
||||
ys = ys[:i]
|
||||
xs = xs[:i] * self.RATE / self.BUFFERSIZE
|
||||
if div_by:
|
||||
ys = ys / float(div_by)
|
||||
return xs, ys
|
||||
|
||||
### multithreading things ###
|
||||
|
||||
def registerRecorderClient(self,recorderClient):
|
||||
self.recorderClients.append(recorderClient)
|
||||
|
||||
def unregisterRecorderClient(self,recorderClient):
|
||||
self.recorderClients.remove(recorderClient)
|
||||
|
||||
class recorderClient(threading.Thread):
|
||||
def __init__():
|
||||
# toggle to true on beat, false when client got the value
|
||||
self.onBeat = False
|
||||
# when registering the client i want to be able to define how long the avg list should be
|
||||
self.y_max_freq_avg_list = []
|
||||
@@ -0,0 +1,54 @@
|
||||
from rgbUtils.RGBStrip import RGBStrip
|
||||
import time
|
||||
import threading
|
||||
import json
|
||||
class rgbStripController(threading.Thread):
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.rgbStrips = []
|
||||
self.onRGBStripRegisteredHandler = []
|
||||
self.onRGBStripUnRegisteredHandler = []
|
||||
|
||||
def registerRGBStrip(self,rgbStripName,onValuesUpdateHandler):
|
||||
# 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.
|
||||
# the wmos could use the mac adress, if there is a python script it can save the uid
|
||||
# in a file or so.
|
||||
strip = RGBStrip(rgbStripName,onValuesUpdateHandler)
|
||||
self.rgbStrips.append(strip)
|
||||
self.noticeRGBStripRegisteredHandler(strip)
|
||||
return strip
|
||||
|
||||
def unregisterRGBStrip(self,strip):
|
||||
self.rgbStrips.remove(strip)
|
||||
self.noticeRGBStripUnRegisteredHandler(strip)
|
||||
|
||||
# returns all registered rgbStips
|
||||
def getRGBStrips(self):
|
||||
return self.rgbStrips
|
||||
|
||||
# inform all onRGBStripRegisteredHandler about the new RGBStrip
|
||||
def noticeRGBStripRegisteredHandler(self,rgbStrip):
|
||||
for hander in self.onRGBStripRegisteredHandler:
|
||||
hander(rgbStrip)
|
||||
|
||||
# add onRGBStripRegisteredHandler
|
||||
def addOnRGBStripRegisteredHandler(self, function):
|
||||
self.onRGBStripRegisteredHandler.append(function)
|
||||
|
||||
# remove onRGBStripRegisteredHandler
|
||||
def removeOnRGBStripRegisteredHandler(self, function):
|
||||
self.onRGBStripRegisteredHandler.remove(function)
|
||||
|
||||
# inform all onRGBStripUnRegisteredHandder about the removed RGBStrip
|
||||
def noticeRGBStripUnRegisteredHandler(self,rgbStrip):
|
||||
for hander in self.onRGBStripUnRegisteredHandler:
|
||||
hander(rgbStrip)
|
||||
|
||||
# add onRGBStripUnRegisteredHandler
|
||||
def addOnRGBStripUnRegisteredHandler(self, function):
|
||||
self.onRGBStripUnRegisteredHandler.append(function)
|
||||
|
||||
# remove onRGBStripUnRegisteredHandler
|
||||
def removeOnRGBStripUnRegisteredHandler(self, function):
|
||||
self.onRGBStripUnRegisteredHandler.remove(function)
|
||||
@@ -0,0 +1,16 @@
|
||||
def getRGBData(rgbStrip):
|
||||
result = {}
|
||||
data = rgbStrip.getData()
|
||||
red = data[0]
|
||||
green = data[1]
|
||||
blue = data[2]
|
||||
for x in range(len(red)):
|
||||
result[x] = {'red': red[x], 'green': green[x], 'blue': blue[x]}
|
||||
return result
|
||||
|
||||
# return json of all configured rgbStrips
|
||||
def getRGBStrips(rgbStripController):
|
||||
result = {}
|
||||
for rgbStrip in rgbStripController.getRGBStrips():
|
||||
result[rgbStrip.STRIP_UID] = {'index': rgbStrip.STRIP_UID, 'name': rgbStrip.STRIP_NAME}
|
||||
return result
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user