1 Commits

Author SHA1 Message Date
siinus
f5a70470d2 Replace hexToRgb function to work with legacy mobile browsers 2021-03-19 20:37:11 +02:00
11 changed files with 56 additions and 188 deletions

View File

@@ -8,9 +8,20 @@ Projekt Litsimaja - Lapikute tagatoa seintele programmeeritavad ARGB ribad
#### Running with emulation #### Running with emulation
This is mainly for testing, development. This is mainly for testing, development.
Create .env file: In ``lib/Litsimaja.py`` change the following:
```cp .env.example .env``` ```
# from lib.strip.TkinterStrip import TkinterStrip
and see: ```USE_EMULATOR``` def __init__(self):
self._strip = PixelStrip(290, 18, 800000, 10, False, 255, 0, 4104)
```
to
```
from lib.strip.TkinterStrip import TkinterStrip
def __init__(self):
self._strip = TkinterStrip(290, 18, 800000, 10, False, 255, 0, 4104)
```
Now when you run the program, you will see a Tkinter window pop up with a rectangle simulating the LED strip. Now when you run the program, you will see a Tkinter window pop up with a rectangle simulating the LED strip.
Don't commit this change.

View File

@@ -1,17 +0,0 @@
ENVIRONMENT=prod
USE_EMULATOR=false
BIND_ADDR=127.0.0.1
BIND_PORT=8080
# Strip configuration
STRIP_PIXELS=290
STRIP_GPIO_PIN=18
STRIP_HZ=800000
STRIP_DMA=10
STRIP_INVERT=false
STRIP_BRIGHTNESS=255
STRIP_CHANNEL=0
STRIP_TYPE=4104
REGIONS=46,96,191,241
BPM_DEFAULT=60

View File

@@ -1,2 +0,0 @@
ENVIRONMENT=dev
USE_EMULATOR=true

1
pyleds/.gitignore vendored
View File

@@ -1,2 +1 @@
.env
litsimaja.log litsimaja.log

View File

@@ -1,38 +0,0 @@
from dotenv import dotenv_values
import pathlib
def populate_values(load: {}):
conf = load
conf['IS_DEV']: bool = str.lower(load['ENVIRONMENT']) == 'dev'
conf['USE_EMULATOR']: bool = str.lower(load['USE_EMULATOR']) == 'true'
conf['BIND_PORT']: int = int(load['BIND_PORT'])
conf['STRIP_PIXELS']: int = int(load['STRIP_PIXELS'])
conf['STRIP_GPIO_PIN']: int = int(load['STRIP_GPIO_PIN'])
conf['STRIP_HZ']: int = int(load['STRIP_HZ'])
conf['STRIP_DMA']: int = int(load['STRIP_DMA'])
conf['STRIP_INVERT']: bool = str.lower(load['STRIP_INVERT']) == 'true'
conf['STRIP_BRIGHTNESS']: int = int(load['STRIP_BRIGHTNESS'])
conf['STRIP_CHANNEL']: int = int(load['STRIP_CHANNEL'])
conf['STRIP_TYPE']: int = int(load['STRIP_TYPE'])
conf['REGIONS']: [] = [int(x) for x in load['REGIONS'].split(',')]
conf['BPM_DEFAULT']: int = int(load['BPM_DEFAULT'])
return conf
class Config(object):
_config: {} = {}
def __init__(self):
rp = str(pathlib.Path(__file__).parent.parent.absolute())
load_conf = {
**dotenv_values(rp + "/.env.defaults"),
**dotenv_values(rp + "/.env"),
}
self._config = populate_values(load_conf)
def get(self, key: str):
return self._config[key]

View File

@@ -1,4 +1,6 @@
from lib.Config import Config from rpi_ws281x import PixelStrip
# from lib.strip.WindowStrip import WindowStrip
# from lib.strip.TkinterStrip import TkinterStrip
from lib.LoopSwitch import LoopSwitch from lib.LoopSwitch import LoopSwitch
from lib.Regions import Regions from lib.Regions import Regions
from lib.Tempo import Tempo from lib.Tempo import Tempo
@@ -6,25 +8,11 @@ from lib.Tempo import Tempo
class Litsimaja(object): class Litsimaja(object):
def __init__(self): def __init__(self):
self._config = Config() self._strip = PixelStrip(290, 18, 800000, 10, False, 255, 0, 4104)
if self.conf('USE_EMULATOR'):
module = __import__('lib.strip.TkinterStrip', None, None, ['TkinterStrip'])
class_name = 'TkinterStrip'
else:
module = __import__('rpi_ws281x', None, None, ['PixelStrip'])
class_name = 'PixelStrip'
loaded_class = getattr(module, class_name)
self._strip = loaded_class(
self.conf('STRIP_PIXELS'), self.conf('STRIP_GPIO_PIN'), self.conf('STRIP_HZ'), self.conf('STRIP_DMA'),
self.conf('STRIP_INVERT'), self.conf('STRIP_BRIGHTNESS'), self.conf('STRIP_CHANNEL'),
self.conf('STRIP_TYPE')
)
self._loops = [] self._loops = []
self._strip.begin() self._strip.begin()
self._regions: Regions = Regions(self.count_pixels(), self.conf('REGIONS')) self._regions: Regions = Regions(self.count_pixels(), [46, 96, 191, 241])
self._tempo: Tempo = Tempo(self.conf('BPM_DEFAULT')) self._tempo: Tempo = Tempo(60)
self._selected_program = None
def count_pixels(self) -> int: def count_pixels(self) -> int:
return self._strip.numPixels() return self._strip.numPixels()
@@ -59,10 +47,9 @@ class Litsimaja(object):
def build_status_array(self): def build_status_array(self):
data = {'success': True} data = {'success': True}
features = { features = {
'program': self._selected_program,
'tempo': { 'tempo': {
'bpm': self.get_tempo().get_bpm() 'bpm': self.get_tempo().get_bpm()
}, }
} }
regions = [] regions = []
for region_id in self._regions.list_region_ids(): for region_id in self._regions.list_region_ids():
@@ -73,9 +60,3 @@ class Litsimaja(object):
def get_tempo(self): def get_tempo(self):
return self._tempo return self._tempo
def set_selected_program(self, program_name: str):
self._selected_program = program_name
def conf(self, key: str):
return self._config.get(key)

View File

@@ -15,7 +15,6 @@ def run(namespace: str, class_name: str, lm: Litsimaja, logger, args: [] = None)
program = loaded_class(lm) program = loaded_class(lm)
logger.info('Loaded "' + module.name() + '" from ' + namespace + '.' + class_name + ' with args: ' + repr(args)) logger.info('Loaded "' + module.name() + '" from ' + namespace + '.' + class_name + ' with args: ' + repr(args))
lm.add_loop(program.get_loop()) lm.add_loop(program.get_loop())
lm.set_selected_program(namespace + '.' + class_name)
program.run(args) program.run(args)

View File

@@ -1,38 +0,0 @@
from lib.Program import Program
from rpi_ws281x import Color
import time
def name():
return 'Christmas Lights'
class ChristmasLights(Program):
def run(self, args: [] = None):
# Configuration
wait_ms = 0 # Speed of the animation
# Define classic festive colors
RED = Color(255, 0, 0)
GREEN = Color(0, 255, 0)
WARM_WHITE = Color(200, 180, 60) # A golden-ish warm white
colors = [RED, GREEN, WARM_WHITE]
# This determines how many pixels of the same color are next to each other
group_size = 2
offset = 0
while self.get_loop().status():
num_pixels = self._lm.count_pixels()
for i in range(num_pixels):
# Calculate which color index to use based on pixel position and current offset
# This creates the "moving" effect
color_index = ((i + offset) // group_size) % len(colors)
self._lm.set_pixel_color(i, colors[color_index])
self._lm.show()
# Increment offset to move the lights
offset = (offset + 1) % (len(colors) * group_size)
self._lm.get_tempo().wait()
time.sleep(wait_ms / 1000.0)

View File

@@ -1,26 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import logging import logging
import pathlib
import sys import sys
import lib.ProgramLoading as Pl import lib.ProgramLoading as Pl
from lib.Litsimaja import Litsimaja from lib.Litsimaja import Litsimaja
from flask import Flask, request, Response, render_template, json from flask import Flask, request, Response, render_template, json
from flask_accept import accept
root_path = pathlib.Path(__file__).parent.absolute()
# start litsimaja
lm = Litsimaja()
# logging # logging
logger = logging.getLogger('litsimaja') logger = logging.getLogger('litsimaja')
logger.setLevel(logging.DEBUG if lm.conf('IS_DEV') else logging.WARN) logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler = logging.FileHandler(str(root_path) + '/litsimaja.log') file_handler = logging.FileHandler('litsimaja.log')
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
logger.addHandler(file_handler) logger.addHandler(file_handler)
stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter) stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler) logger.addHandler(stdout_handler)
# start litsimaja
lm = Litsimaja()
app = Flask(__name__, static_url_path='', static_folder='templates') app = Flask(__name__, static_url_path='', static_folder='templates')
Pl.run('siinus', 'Wipes', lm, logger, {'color': [0, 0, 0]}) Pl.run('siinus', 'Wipes', lm, logger, {'color': [0, 0, 0]})
Pl.run('peter', 'DiskoPidu', lm, logger, {}) Pl.run('peter', 'DiskoPidu', lm, logger, {})
@@ -31,6 +30,7 @@ def lm_standard_xhr_response() -> Response:
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
@accept('text/html')
def respond_root(): def respond_root():
return render_template( return render_template(
'index.html', 'index.html',
@@ -41,6 +41,7 @@ def respond_root():
@app.route('/status', methods=['GET']) @app.route('/status', methods=['GET'])
@accept('application/json')
def respond_status(): def respond_status():
return lm_standard_xhr_response() return lm_standard_xhr_response()
@@ -78,4 +79,4 @@ def sync_beat():
return lm_standard_xhr_response() return lm_standard_xhr_response()
app.run(lm.conf('BIND_ADDR'), lm.conf('BIND_PORT')) app.run('0.0.0.0', 8080)

View File

@@ -1,3 +1,13 @@
.section-rgb {
height: 20rem;
text-align: center;
}
select {
width: 100%;
height: calc(100% - 1.5rem);
}
.colorpicker { .colorpicker {
display: inline-block; display: inline-block;
height: 38px; height: 38px;
@@ -26,24 +36,3 @@ button.region {
button.region_off { button.region_off {
background-color: gray !important background-color: gray !important
} }
div.program_select {
height: 300px;
overflow: auto;
border: 1px solid gray;
text-align: left;
}
div.program_select ul {
list-style: none;
border-bottom: 1px solid silver;
margin-bottom: 0;
}
div.program_select ul li {
margin: auto;
}
div.program_select ul li label {
margin: 3px auto;
}

View File

@@ -7,7 +7,6 @@
<link rel="stylesheet" href="custom.css"> <link rel="stylesheet" href="custom.css">
<script type="text/javascript"> <script type="text/javascript">
'use strict';
function hexToRgb(value) { function hexToRgb(value) {
var aRgbHex = value.substring(1).match(/.{1,2}/g); var aRgbHex = value.substring(1).match(/.{1,2}/g);
var aRgb = [ var aRgb = [
@@ -55,29 +54,19 @@
let status = JSON.parse(json); let status = JSON.parse(json);
document.getElementById('tempo').value = status.features.tempo.bpm; document.getElementById('tempo').value = status.features.tempo.bpm;
let regions = status.features.region; let regions = status.features.region;
for (let i = 0, len = regions.length; i < len; i++) { let regLen = regions.length;
for(i = 0; i < regLen; i++) {
if (regions[i]) { if (regions[i]) {
document.getElementById('region_' + i).classList.remove('region_off'); document.getElementById('region_' + i).classList.remove('region_off');
} else { } else {
document.getElementById('region_' + i).classList.add('region_off'); document.getElementById('region_' + i).classList.add('region_off');
} }
} }
let programs = document.getElementsByName('program_select');
for (var i = 0, len = programs.length; i < len; i++) {
if (programs[i].value === status.features.program) {
programs[i].checked = true;
}
}
} }
function getProgramSelection() { function getProgramSelection() {
let radios = document.getElementsByName('program_select'); let select = document.getElementById('program');
for (var i = 0, length = radios.length; i < length; i++) { return select.value;
if (radios[i].checked) {
return radios[i].value;
}
}
return null;
} }
function isLoop() { function isLoop() {
@@ -134,25 +123,19 @@
{% endfor %} {% endfor %}
</div> </div>
<div class="row" id='loading' style="visibility: hidden"><b>LOADING!</b></div> <div class="row" id='loading' style="visibility: hidden"><b>LOADING!</b></div>
<div class="section-rgb">
<div class="row"> <div class="row">
<div class="program_select one-half column"> <div class="one-third column">
<select id="program" size="10">
{% for group in programs %} {% for group in programs %}
<ul> <optgroup label="{{ group.group }}">
<li>{{ group.group }}</li>
{% for program in group.programs %} {% for program in group.programs %}
{% set pr_checked = '' %} <option value="{{ program.prg }}">{{ program.name }}</option>
{% if status.features.program == program.prg %}
{% set pr_checked = ' checked' %}
{% endif %}
<li>
<label>
<input type="radio" name="program_select" value="{{ program.prg }}"{{ pr_checked }}>
{{ program.name }}
</label>
</li>
{% endfor %} {% endfor %}
</ul> </optgroup>
{% endfor %} {% endfor %}
</select>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">