Compare commits
12 commits
Author | SHA1 | Date | |
---|---|---|---|
Stefan Bethke | d6cd834ca8 | ||
Stefan Bethke | a290dccb67 | ||
Stefan Bethke | a29cbd9683 | ||
Stefan Bethke | 496e41bd4b | ||
Stefan Bethke | 4906c633e2 | ||
Stefan Bethke | e9f4786788 | ||
Stefan Bethke | 8c2c7fb8bc | ||
Stefan Bethke | 61f4d5dde3 | ||
Stefan Bethke | 69914c6545 | ||
Dario | 7fbee99978 | ||
yuri | bf6170aa12 | ||
yuri | feced317af |
154
animation.py
154
animation.py
|
@ -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
83
dmx.py
|
@ -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
9
foobaz-shop.service
Normal 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
5
foobaz-shop.sh
Executable 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
|
52
foobaz.py
52
foobaz.py
|
@ -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:])
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue