Compare commits

..

12 commits
groups ... main

Author SHA1 Message Date
Stefan Bethke d6cd834ca8 Remove super annoying effect
Slow down rainbow

Closes #3
2024-08-11 22:00:30 +02:00
Stefan Bethke a290dccb67 Variable korrekt deklarieren 2024-02-27 19:53:28 +01:00
Stefan Bethke a29cbd9683 Raum mit anzeigen 2024-02-27 19:42:19 +01:00
Stefan Bethke 496e41bd4b Use correct option 2024-02-26 23:00:59 +01:00
Stefan Bethke 4906c633e2 Use correct option 2024-02-26 23:00:27 +01:00
Stefan Bethke e9f4786788 Make port configurable 2024-02-26 22:58:14 +01:00
Stefan Bethke 8c2c7fb8bc Correct typo 2024-02-26 22:56:17 +01:00
Stefan Bethke 61f4d5dde3 make universe configurable 2024-02-26 22:52:47 +01:00
Stefan Bethke 69914c6545 Unit für den Werkstatt-Server 2024-02-26 21:33:15 +00:00
Dario 7fbee99978 Merge branch 'css-changes' into 'main'
CSS changes

See merge request ccchh/foobazdmx!1
2023-05-20 23:40:29 +00:00
yuri bf6170aa12
change twocolor name 2022-11-22 20:29:14 +01:00
yuri feced317af
added 3 new animations, simple css change 2022-11-22 20:24:53 +01:00
7 changed files with 250 additions and 91 deletions

View file

@ -1,5 +1,7 @@
import random
import colorsys import colorsys
from time import time from time import time
from typing import Tuple
class Animation: class Animation:
@ -41,6 +43,135 @@ class Steady(Animation):
def name(self): def name(self):
return "steady" return "steady"
class RandomSingle(Animation):
"""
by Max & Lightmoll (https://lght.ml)
from 2022-06-08
"""
def __init__(self, color):
super().__init__()
self.PERIOD = 4 * 30 #frames
self.color = self._rand_color()
self.last_colors = []
self.frame_counter = 0
def update(self, index:int, count:int) -> Tuple[int, int, int]:
if index == 0:
if ((self.frame_counter % self.PERIOD) == 0):
self.last_colors = []
for _ in range(count):
self.last_colors.append(self._rand_color())
self.frame_counter += 1
try:
return self.last_colors[index]
except IndexError:
print("INDEX ERROR: ", index, len(self.last_colors))
raise Exception(f"{index}, {len(self.last_colors)}")
def _rand_color(self):
return (
round(random.random() * 255),
round(random.random() * 255),
round(random.random() * 255)
)
def __str__(self):
return "randomsingle"
def name(self):
return str(self)
class TwoColor(Steady):
"""
by Max & Lightmoll (https://lght.ml)
from 2022-06-08
"""
def __init__(self, color):
super().__init__(color)
self.start_time = time()
self.PERIOD = 0.5 #s
self.COL_1 = color #input color
self.COL_2 = (255-self.r, 255-self.g, 255-self.b) #compl. color
#of lights
def update(self, index:int, count:int) -> Tuple[int, int, int]:
time_diff = time() - self.start_time
#r,g,b
color = (0,0,0)
if (self.PERIOD / 2 > (time_diff % self.PERIOD)):
color = self.COL_1
else:
color = self.COL_2
return color
def __str__(self):
return "twocolor"
def name(self):
return str(self)
class Caramelldansen(Steady):
"""
by Max & Lightmoll (https://lght.ml)
from 2022-06-08
"""
def __init__(self, color):
super().__init__(color)
self.start_time = time()
self.PERIOD = int(0.42 * 30) #frames
self.COLORS = [
(230,50,50),
(255,0,0),
(50,230,50),
(0,255,0),
(50,50,230),
(0,0,255)
]
"""
self.COLORS = [
(255,0,0),
(0,255,0),
(0,0,255)
]
"""
self.color_index = 0
self.frame_counter = 0
def update(self, index:int, count:int) -> Tuple[int, int, int]:
time_diff = round(time() - self.start_time)
#r,g,b
if index == 0:
self.frame_counter += 1
if (((self.frame_counter % self.PERIOD) == 0) and index == 0):
self.color_index += 1
if (self.color_index == len(self.COLORS)):
#self.frame_counter = 0 #prevent big numbers
self.color_index = 0
return self.COLORS[self.color_index]
def __str__(self):
# when is this endpoint called?
return "caramelldansen"
def name(self):
return str(self)
class FadeTo(Steady): class FadeTo(Steady):
def __init__(self, color, t=2.0): def __init__(self, color, t=2.0):
@ -61,7 +192,7 @@ class FadeTo(Steady):
class RotatingRainbow(Animation): class RotatingRainbow(Animation):
def __init__(self, looptime=10.0): def __init__(self, looptime=50.0):
super(RotatingRainbow, self).__init__() super(RotatingRainbow, self).__init__()
self.looptime = looptime self.looptime = looptime
pass pass
@ -102,6 +233,27 @@ class Chase(Steady):
return "chase" return "chase"
class ChaseRandom(Animation):
def __init__(self, color, looptime=1.0):
super(Chase, self).__init__(color)
self.looptime = looptime
def update(self, index, count):
angle = (time() / self.looptime + (index + 0.0) / count) % 1.0
l = 1 - min(abs(angle - 1 / count) * .9, 1.0 / count) * count
# print(f"f({index}, {angle:.2f}) -> {l:.2f}")
return (int(self.r * l), int(self.g * l), int(self.b * l))
def __str__(self):
return f"{type(self).__name__}({self.r}, {self.g}, {self.b}, {self.looptime:.2f})"
def name(self):
return "chase"
def hsv_to_rgb(h, s, v): def hsv_to_rgb(h, s, v):
(r, g, b) = colorsys.hsv_to_rgb(h, s, v) (r, g, b) = colorsys.hsv_to_rgb(h, s, v)
return [int(r * 255), int(g * 255), int(b * 255)] return [int(r * 255), int(g * 255), int(b * 255)]
def rgb_to_hsv(r, g, b):
return colorsys.rgb_to_hsv(r/255,g/255,b/255)

83
dmx.py
View file

@ -5,7 +5,7 @@ from threading import Thread
from time import sleep from time import sleep
from typing import Union from typing import Union
from animation import Animation, Off, Steady, FadeTo, RotatingRainbow, Chase from animation import Animation, Off, RandomSingle, Steady, FadeTo, RotatingRainbow, Chase, TwoColor, Caramelldansen
def ledlog(value): def ledlog(value):
@ -13,23 +13,21 @@ def ledlog(value):
class RGB: class RGB:
def __init__(self, slot, offset=0): def __init__(self, dmx, slot, offset=0):
self.dmx = dmx
self.slot = slot self.slot = slot
self.offset = offset self.offset = offset
def rgb(self, dmx, color): def rgb(self, color):
(r, g, b) = color (r, g, b) = color
dmx.set(self.slot + self.offset + 0, ledlog(r)) self.dmx.set(self.slot + self.offset + 0, ledlog(r))
dmx.set(self.slot + self.offset + 1, ledlog(g)) self.dmx.set(self.slot + self.offset + 1, ledlog(g))
dmx.set(self.slot + self.offset + 2, ledlog(b)) self.dmx.set(self.slot + self.offset + 2, ledlog(b))
class Bar252(RGB): class Bar252(RGB):
def __init__(self, dmx, slot=1): def __init__(self, dmx, slot=1):
super(Bar252, self).__init__(dmx, slot, 2) super(Bar252, self).__init__(dmx, slot, 2)
def rgb(self, dmx, color):
super().rgb(dmx, color)
dmx.set(self.slot + 0, 81) dmx.set(self.slot + 0, 81)
dmx.set(self.slot + 1, 0) dmx.set(self.slot + 1, 0)
@ -39,10 +37,6 @@ class REDSpot18RGB(RGB):
super(REDSpot18RGB, self).__init__(dmx, slot, 1) super(REDSpot18RGB, self).__init__(dmx, slot, 1)
dmx.set(self.slot + 0, 0) dmx.set(self.slot + 0, 0)
def rgb(self, dmx, color):
super().rgb(dmx, color)
dmx.set(self.slot + 0, 81)
class StairvilleLedPar56(RGB): class StairvilleLedPar56(RGB):
def __init__(self, dmx, slot=1): def __init__(self, dmx, slot=1):
@ -52,28 +46,6 @@ class StairvilleLedPar56(RGB):
dmx.set(self.slot + 5, 0) dmx.set(self.slot + 5, 0)
dmx.set(self.slot + 6, 255) dmx.set(self.slot + 6, 255)
class Group:
def __init__(self, rgbs : list[RGB]):
self._rgbs = rgbs
self._color = (0, 0, 0)
self._animation = FadeTo((255, 255, 255))
@property
def animation(self) -> Animation:
return self._animation
@animation.setter
def animation(self, animation: Animation):
self._animation = animation
@property
def color(self) -> list[int]:
return self._color
@color.setter
def color(self, color: list[int]):
self._color = color
class DMX: class DMX:
def __init__(self, host, port=0x1936, universe=1, maxchan=512): def __init__(self, host, port=0x1936, universe=1, maxchan=512):
@ -90,13 +62,12 @@ class DMX:
packet.extend([0x00, 0x0e]) # Protocol version 14 packet.extend([0x00, 0x0e]) # Protocol version 14
self._header = packet self._header = packet
self._sequence = 1 self._sequence = 1
self._groups = [] self._color = (0, 0, 0)
self._animation = FadeTo((255, 255, 255))
self._rgbs = []
self._thread = None self._thread = None
self._updating = False self._updating = False
def group(self, group: int, rgbs : list[RGB]):
self._groups[group] = Group(rgbs)
def start(self): def start(self):
if self._thread and self._thread.is_alive(): if self._thread and self._thread.is_alive():
return return
@ -113,12 +84,11 @@ class DMX:
sleep(1.0 / 30) sleep(1.0 / 30)
def update(self): def update(self):
if not self._groups[0]._animation: if not self._animation:
return return
for g in self._groups: for i in range(0, len(self._rgbs)):
for i in range(0, len(g._rgbs)): self._rgbs[i].rgb(self._animation.update(i, len(self._rgbs)))
g._rgbs[i].rgb(self, g._animation.update(i, len(g._rgbs)))
packet = self._header[:] packet = self._header[:]
packet.append(self._sequence) # Sequence, packet.append(self._sequence) # Sequence,
@ -140,7 +110,7 @@ class DMX:
@property @property
def animation(self) -> str: def animation(self) -> str:
return self._groups[0]._animation.name() return self._animation.name()
@animation.setter @animation.setter
def animation(self, animation: Union[Animation, str]): def animation(self, animation: Union[Animation, str]):
@ -148,35 +118,40 @@ class DMX:
if animation == "off": if animation == "off":
animation = Off() animation = Off()
elif animation == "chase": elif animation == "chase":
animation = Chase(self._groups[0]._color) animation = Chase(self._color)
elif animation == "fade": elif animation == "fade":
animation = FadeTo(self._groups[0]._color) animation = FadeTo(self._color)
elif animation == "rainbow": elif animation == "rainbow":
animation = RotatingRainbow() animation = RotatingRainbow()
elif animation == "steady": elif animation == "steady":
animation = Steady(self._groups[0]._color) animation = Steady(self._color)
elif animation == "twocolor":
animation = TwoColor(self._color)
elif animation == "caramelldansen":
animation = Caramelldansen(self._color)
elif animation == "randomsingle":
animation = RandomSingle(self._color)
else: else:
raise ValueError(f"No such animation {animation}") raise ValueError(f"No such animation {animation}")
self._groups[0]._animation = animation self._animation = animation
if isinstance(animation, Off): if isinstance(animation, Off):
self._updating = False self._updating = False
if self._thread and self._thread.is_alive(): if self._thread and self._thread.is_alive():
self._thread.join() self._thread.join()
# one frame black # one frame black
self._groups[0]._animation = Steady((0, 0, 0)) self._animation = Steady((0, 0, 0))
self.update() self.update()
self._groups[0]._animation = Off() self._animation = Off()
else: else:
self.start() self.start()
print(f"Animation: {animation}", file=sys.stderr) print(f"Animation: {animation}", file=sys.stderr)
@property @property
def color(self): def color(self):
return self._groups[0].color return self._color
@color.setter @color.setter
def color(self, color): def color(self, color):
if self._groups[0]._color != color: if self._color != color:
self._groups[0].color = color self._color = color
self.animation = self.animation self.animation = self.animation

9
foobaz-shop.service Normal file
View file

@ -0,0 +1,9 @@
[Unit]
Description=Foobaz DMX
[Service]
ExecStart=/home/pi/foobazdmx/foobaz-shop.sh
User=pi
[Install]
WantedBy=multi-user.target

5
foobaz-shop.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
here=$(cd $(dirname $0); pwd)
cd $here
exec poetry run python foobaz.py -a 127.0.0.1 -l 8081 -r shop -u 2

View file

@ -9,11 +9,12 @@ from bottle import post, request, route, run, static_file, view
from animation import Off from animation import Off
from dmx import DMX, Bar252, StairvilleLedPar56, REDSpot18RGB from dmx import DMX, Bar252, StairvilleLedPar56, REDSpot18RGB
room = ''
@route('/') @route('/')
@view('index') @view('index')
def index(): def index():
return {'foo': 'bar'} return {'room': room}
@route('/static/<path:path>') @route('/static/<path:path>')
def static(path): def static(path):
@ -43,46 +44,45 @@ def rgb_to_hex(color: Tuple[int, int, int]) -> str:
return f"#{color[0]:02X}{color[1]:02X}{color[2]:02X}" return f"#{color[0]:02X}{color[1]:02X}{color[2]:02X}"
def main(args): def main(args):
global dmx global dmx, room
parser = argparse.ArgumentParser(prog='foobazdmx', description='Make the lights flicker.') parser = argparse.ArgumentParser(prog='foobazdmx', description='Make the lights flicker.')
parser.add_argument('-a', '--artnet', type=str, required=True, help="Art-Net server") parser.add_argument('-a', '--artnet', type=str, required=True, default="127.0.0.1", help="Art-Net server")
parser.add_argument('-l', '--listen', type=int, required=False, default=8080, help="TCP port to listen on for web")
parser.add_argument('-r', '--room', type=str, required=True, help="light setup for room: shop or big") parser.add_argument('-r', '--room', type=str, required=True, help="light setup for room: shop or big")
parser.add_argument('-u', '--universe', type=int, required=False, default=1, help="Universe to send to")
args = parser.parse_args(args) args = parser.parse_args(args)
artnet = args.artnet print(f"Starting DMX via Art-Net to {args.artnet}", file=sys.stderr)
dmx = DMX(args.artnet, maxchan=128, universe=args.universe)
print(f"Starting DMX via Art-Net to {artnet}", file=sys.stderr)
dmx = DMX(artnet, maxchan=128)
if args.room == "shop": if args.room == "shop":
dmx.group(0, [ dmx._rgbs = [
REDSpot18RGB(1), REDSpot18RGB(dmx, 1),
REDSpot18RGB(5), REDSpot18RGB(dmx, 5),
REDSpot18RGB(9), REDSpot18RGB(dmx, 9),
REDSpot18RGB(13), REDSpot18RGB(dmx, 13),
]) ]
elif args.room == "big": elif args.room == "big":
dmx.group(0, [ dmx._rgbs = [
StairvilleLedPar56(1), # Window/Kitchen StairvilleLedPar56(dmx, 1),
StairvilleLedPar56(15), # Hallway/Kitchen StairvilleLedPar56(dmx, 8),
Bar252(29), # Window/Blackboard StairvilleLedPar56(dmx, 15),
Bar252(40), # Hallway/Blackboard StairvilleLedPar56(dmx, 22),
Bar252(51), # Window/Kitchen Bar252(dmx, 29),
Bar252(62), # Hallway/Kitchen Bar252(dmx, 40),
]) Bar252(dmx, 51),
dmx.group(0, [ Bar252(dmx, 62),
StairvilleLedPar56(8), # Window/Blackboard ]
StairvilleLedPar56(22), # Hallway/Blackboard
])
else: else:
print(f"Unknown room {args.room}", file=sys.stderr) print(f"Unknown room {args.room}", file=sys.stderr)
sys.exit(64) sys.exit(64)
room = args.room
dmx.animation = Off() dmx.animation = Off()
dmx.color = (0, 0, 0) dmx.color = (0, 0, 0)
dmx.animation = "off" dmx.animation = "off"
run(host='0.0.0.0', port=8080, reloader=False, debug=True) run(host='0.0.0.0', port=args.listen, reloader=False, debug=True)
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv[1:]) main(sys.argv[1:])

View file

@ -1,8 +1,23 @@
body { :root {
background: black; --bg-color: #403F3C;
color: white; --primary-color: #ccc;
} }
html {
background: var(--bg-color);
}
body {
padding-top: 20px;
width: 80%;
height: 100%;
color: var(--primary-color);
margin: auto;
font-family: "monospace";
}
.controls { .controls {
display: flex; display: flex;
width: 40em; width: 40em;
@ -10,14 +25,21 @@ body {
fieldset { fieldset {
flex: auto; flex: auto;
margin-right: 5px;
} }
.buttons { .buttons {
width: 30em; width: 30em;
} }
input.button_color { input.button_color {
width: 6em; width: 6em;
margin: 4px;
border: none;
padding: 5px 10px;
/* border-radius: 5px; */
} }
.button_color_white { .button_color_white {

View file

@ -1,11 +1,11 @@
<html> <html>
<head> <head>
<title>Foo Baz DMX</title> <title>Foo Baz DMX - {{room}}</title>
<link rel="stylesheet" href="static/main.css"> <link rel="stylesheet" href="static/main.css">
<script src="static/main.js" defer></script> <script src="static/main.js" defer></script>
</head> </head>
<body> <body>
<h1>Foo Baz DMX</h1> <h1>Foo Baz DMX - {{room}}</h1>
<div class="controls"> <div class="controls">
<fieldset class="buttons"> <fieldset class="buttons">
<legend>Animation</legend> <legend>Animation</legend>
@ -17,10 +17,6 @@
<input type="radio" class="js_animation" name="state" id="animation_white" value="steady"/> <input type="radio" class="js_animation" name="state" id="animation_white" value="steady"/>
<label for="animation_white">Steady</label> <label for="animation_white">Steady</label>
</div> </div>
<div>
<input type="radio" class="js_animation" name="state" id="animation_chase" value="chase"/>
<label for="animation_chase">Chase</label>
</div>
<div> <div>
<input type="radio" class="js_animation" name="state" id="animation_rainbow" value="rainbow"/> <input type="radio" class="js_animation" name="state" id="animation_rainbow" value="rainbow"/>
<label for="animation_rainbow">Rainbow</label> <label for="animation_rainbow">Rainbow</label>