Add python led programs

pull/1/head
siinus 5 years ago
parent 1fc4303ae2
commit 103a5c9252
  1. 1
      pyleds/.gitignore
  2. 94
      pyleds/lib/FakeStrip.py
  3. 33
      pyleds/lib/Litsimaja.py
  4. 11
      pyleds/lib/LoopSwitch.py
  5. 17
      pyleds/lib/Program.py
  6. 40
      pyleds/lib/ProgramLoading.py
  7. 13
      pyleds/program/siinus/Static.py
  8. 37
      pyleds/program/siinus/Wipes.py
  9. 57
      pyleds/run.py
  10. 103
      pyleds/templates/index.html
  11. 3180
      pyleds/templates/jscolor.js

1
pyleds/.gitignore vendored

@ -0,0 +1 @@
litsimaja.log

@ -0,0 +1,94 @@
from rpi_ws281x import PixelStrip
import atexit
class _LedData(object):
_led: []
def __init__(self, channel, size):
self.size = size
self.channel = channel
def __getitem__(self, pos):
"""Return the 24-bit RGB color value at the provided position or slice
of positions.
"""
# Handle if a slice of positions are passed in by grabbing all the values
# and returning them in a list.
if isinstance(pos, slice):
return [self._led[n] for n in range(*pos.indices(self.size))]
# Else assume the passed in value is a number to the position.
else:
return self._led[pos]
def __setitem__(self, pos, value):
"""Set the 24-bit RGB color value at the provided position or slice of
positions.
"""
# Handle if a slice of positions are passed in by setting the appropriate
# LED data values to the provided values.
if isinstance(pos, slice):
index = 0
for n in range(*pos.indices(self.size)):
self._led[n] = value[index]
index += 1
# Else assume the passed in value is a number to the position.
# else:
self._led[pos] = value
class FakeStrip(PixelStrip):
_brightness: int
def __init__(self, num, pin, freq_hz=800000, dma=10, invert=False,
brightness=255, channel=0, strip_type=None, gamma=None):
if gamma is None:
# Support gamma in place of strip_type for back-compat with
# previous version of forked library
if type(strip_type) is list and len(strip_type) == 256:
gamma = strip_type
# strip_type = None
else:
gamma = list(range(256))
# Initialize the channel in use
self._channel = None
self._gamma = gamma
self._brightness = brightness
# Grab the led data array.
self._led_data = _LedData(self._channel, num)
# Substitute for __del__, traps an exit condition and cleans up properly
atexit.register(self._cleanup)
def _cleanup(self):
# Clean up memory used by the library when not needed anymore.
if self._channel is not None:
self._channel = None
def setGamma(self, gamma):
if type(gamma) is list and len(gamma) == 256:
self._gamma = gamma
def begin(self):
"""Initialize library, must be called once before other functions are called."""
return
def show(self):
"""Update the display with the data from the LED buffer."""
return # render
def getBrightness(self) -> int:
return self._brightness
def setBrightness(self, brightness: int):
"""Scale each LED in the buffer by the provided brightness.
A brightness of 0 is the darkest and 255 is the brightest.
"""
self._brightness = brightness
def numPixels(self):
"""Return the number of pixels in the display."""
return self._led_data.size

@ -0,0 +1,33 @@
from rpi_ws281x import PixelStrip
from lib.FakeStrip import FakeStrip
from lib.LoopSwitch import LoopSwitch
class Litsimaja(object):
_strip: PixelStrip
_loops: []
def __init__(self):
self._strip = FakeStrip(290, 18, 800000, 10, False, 255, 0, 4104)
self._loops = []
self._strip.begin()
def count_pixels(self) -> int:
return self._strip.numPixels()
def get_strip(self) -> PixelStrip:
return self._strip
def set_pixel_color(self, n: int, color: int) -> None:
self._strip.setPixelColor(n, color)
def show(self) -> None:
self._strip.show()
def add_loop(self, loop: LoopSwitch):
self._loops.append(loop)
def clear_loops(self):
for loop in self._loops:
loop.stop()
self._loops.clear()

@ -0,0 +1,11 @@
class LoopSwitch(object):
_run: bool
def __init__(self):
self._run = True
def stop(self):
self._run = False
def status(self) -> bool:
return self._run

@ -0,0 +1,17 @@
from lib.Litsimaja import Litsimaja
from lib.LoopSwitch import LoopSwitch
class Program:
_lm: Litsimaja
_loop: LoopSwitch
def __init__(self, lm: Litsimaja):
self._lm = lm
self._loop = LoopSwitch()
def get_loop(self):
return self._loop
def run(self, args: [] = None):
pass

@ -0,0 +1,40 @@
from os import scandir
from lib.Litsimaja import Litsimaja
from lib.Program import Program
def resolve(namespace: str, class_name: str):
m_name = 'program.' + namespace + '.' + class_name
module = __import__(m_name, None, None, [m_name])
return module
def run(namespace: str, class_name: str, lm: Litsimaja, logger, args: [] = None):
module = resolve(namespace, class_name)
loaded_class = getattr(module, class_name)
program = loaded_class(lm)
logger.info('Loaded "' + module.name() + '" from ' + namespace + '.' + class_name + ' with args: ' + repr(args))
lm.add_loop(program.get_loop())
program.run(args)
def list_all(grouped: bool = False):
all_programs = []
programs = []
for entry in scandir('program'):
if entry.is_dir():
for file in scandir('program/' + entry.name):
if file.is_file() and file.name[-3:] == '.py':
try:
program_name = file.name[: -3]
module = resolve(entry.name, program_name)
if hasattr(module, program_name) and issubclass(getattr(module, program_name), Program):
programs.append({'prg': entry.name + '.' + program_name, 'name': module.name()})
except AttributeError:
continue
if grouped and len(programs) > 0:
all_programs.append({'group': entry.name, 'programs': programs})
programs = []
if not grouped:
all_programs = programs
return all_programs

@ -0,0 +1,13 @@
from lib.Program import Program
def name():
return 'Static color'
class Static(Program):
def run(self, args: [] = None) -> None:
color_arr = args['color']
for i in range(self._lm.count_pixels()):
self._lm.set_pixel_color(i, (color_arr[0] << 16) | (color_arr[1] << 8) | color_arr[2])
self._lm.show()

@ -0,0 +1,37 @@
from lib.Program import Program
from lib.Litsimaja import Litsimaja
from rpi_ws281x import Color
def name():
return 'Wipey wipe'
def color_wipe(lm: Litsimaja, color):
for i in range(lm.count_pixels()):
lm.set_pixel_color(i, color)
if i % 4 == 0:
lm.show()
lm.show()
class Wipes(Program):
def run(self, args=None):
loop = False
if 'loop' in args and args['loop']:
loop = args['loop']
if 'color' in args:
end = Color(args['color'][0], args['color'][1], args['color'][2])
else:
end = Color(100, 100, 50)
r = Color(255, 0, 0)
g = Color(0, 255, 0)
b = Color(0, 0, 255)
while self.get_loop().status():
color_wipe(self._lm, r)
color_wipe(self._lm, g)
color_wipe(self._lm, b)
color_wipe(self._lm, end)
if not loop:
break

@ -0,0 +1,57 @@
#!/usr/bin/env python3
import logging
import sys
import json
import lib.ProgramLoading as Pl
from lib.Litsimaja import Litsimaja
from flask import Flask, request, Response, render_template
# logging
logger = logging.getLogger('litsimaja')
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler = logging.FileHandler('litsimaja.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler)
# start litsimaja
lm = Litsimaja()
app = Flask(__name__)
# Pl.run('siinus', 'Static', lm, logger, {'color': [50, 50, 50]})
@app.route('/', methods=['GET'])
def respondroot():
return render_template('index.html', programs=Pl.list_all(True))
@app.route('/jscolor.js', methods=['GET'])
def respondjs():
return render_template('jscolor.js')
@app.route('/run')
def run():
Pl.run('siinus', 'MyProgram', lm, logger, {'color': [50, 50, 50]})
return Response(status=200)
@app.route('/crash', methods=['GET'])
def crash():
lm.clear_loops()
return Response(status=200)
@app.route('/program/<program>', methods=['POST'])
def run_program(program):
args = request.get_json(force=True)
prg = program.split('.')
Pl.run(prg[0], prg[1], lm, logger, args)
return Response(status=200)
app.run('0.0.0.0', 8080)

@ -0,0 +1,103 @@
<html>
<head>
<title>litsimaja</title>
<script src="jscolor.js"></script>
<script type="text/javascript">
jscolor.presets.default = {
format:'rgb', previewPosition:'right', previewSize:700, width:800,
height:400, sliderSize:50
};
function show_loading(visible)
{
document.getElementById('loading').style.visibility = visible ? 'visible' : 'hidden';
}
function send_get(url)
{
show_loading(true);
var http = new XMLHttpRequest();
http.open('GET', url, true);
http.onreadystatechange = function() {
if (http.readyState == 4 && http.status == 200) {
show_loading(false);
}
}
http.send();
}
function send_post(url, data)
{
show_loading(true);
var http = new XMLHttpRequest();
http.open('POST', url, true);
http.setRequestHeader('Content-Type', 'application/json');
http.onreadystatechange = function() {
if (http.readyState == 4 && http.status == 200) {
show_loading(false);
}
}
let json = JSON.stringify(data);
http.send(json);
}
function getProgramSelection()
{
let select = document.getElementById('program');
return select.value;
}
function isLoop()
{
let looping = document.getElementById('looping');
return looping.checked;
}
function isChanged(value)
{
let rgb = value.split('(')[1].split(')')[0].split(',');
let data = {color: [Number(rgb[0]), Number(rgb[1]), Number(rgb[2])], loop: isLoop()};
send_post('program/' + getProgramSelection(), data);
}
function doInit()
{
send_post('/program/siinus/Wipes', {color: [100, 50, 0]})
}
function doBlind()
{
send_post('/program/siinus/Static', {color: [0, 0, 0]})
}
function doCancel()
{
send_get('/crash');
}
</script>
</head>
<body>
<button onclick="doInit()">init</button>
<button onclick="doCancel()">end loop</button>
<button onclick="doBlind()">off</button>
<label><input type="checkbox" id="looping"/>loop</label>
<div id='loading' style="visibility: hidden">LOADING!</div>
<select id="program" size="10">
{% for group in programs %}
<optgroup label="{{ group.group }}">
{% for program in group.programs %}
<option label="{{ program.name }}">{{ program.prg }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
<input id="rgbinput" data-jscolor="" onchange="isChanged(this.value)">
</body>
</html>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save