Refactoring
- Animation in it's own class - JS to sync state across browsers
This commit is contained in:
parent
f4f5458209
commit
efb7c20af5
107
animation.py
Normal file
107
animation.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import colorsys
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
|
||||||
|
class Animation:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{type(self).__name__}"
|
||||||
|
|
||||||
|
def update(self, index, count):
|
||||||
|
return (0, 0 ,0)
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "None"
|
||||||
|
|
||||||
|
|
||||||
|
class Off(Animation):
|
||||||
|
def __init__(self):
|
||||||
|
super(Off, self).__init__()
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "Off"
|
||||||
|
|
||||||
|
|
||||||
|
class Steady(Animation):
|
||||||
|
def __init__(self, color):
|
||||||
|
super(Steady, self).__init__()
|
||||||
|
(r, g, b) = color
|
||||||
|
self.r = r
|
||||||
|
self.g = g
|
||||||
|
self.b = b
|
||||||
|
|
||||||
|
def update(self, index, count):
|
||||||
|
return (self.r, self.g, self.b)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{type(self).__name__}({self.r}, {self.g}, {self.b})"
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "steady"
|
||||||
|
|
||||||
|
|
||||||
|
class FadeTo(Steady):
|
||||||
|
def __init__(self, color, t=2.0):
|
||||||
|
super(FadeTo, self).__init__(color)
|
||||||
|
self.t = t
|
||||||
|
self.start = time()
|
||||||
|
|
||||||
|
def update(self, index, count):
|
||||||
|
h = (time() - self.start) / self.t
|
||||||
|
h = min(h, 1.0)
|
||||||
|
return (int(self.r * h), int(self.g * h), int(self.b * h))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{type(self).__name__}({self.r}, {self.g}, {self.b}, {self.t:.2f})"
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "fadeTo"
|
||||||
|
|
||||||
|
|
||||||
|
class RotatingRainbow(Animation):
|
||||||
|
def __init__(self, looptime=10.0):
|
||||||
|
super(RotatingRainbow, self).__init__()
|
||||||
|
self.looptime = looptime
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, index, count):
|
||||||
|
"""
|
||||||
|
One full round takes self.looptime seconds, each RGB is offset in a circle
|
||||||
|
:param index:
|
||||||
|
:param count:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
hue = (time() / self.looptime + (index + 0.0) / count) % 1.0
|
||||||
|
rgb = hsv_to_rgb(hue, 1, 1)
|
||||||
|
return rgb
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{type(self).__name__}"
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "rainbow"
|
||||||
|
|
||||||
|
|
||||||
|
class Chase(Steady):
|
||||||
|
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):
|
||||||
|
(r, g, b) = colorsys.hsv_to_rgb(h, s, v)
|
||||||
|
return [int(r * 255), int(g * 255), int(b * 255)]
|
176
dmx.py
176
dmx.py
|
@ -1,14 +1,11 @@
|
||||||
import colorsys
|
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep, time
|
from time import sleep
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from animation import Animation, Off, Steady, FadeTo, RotatingRainbow, Chase
|
||||||
def 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)]
|
|
||||||
|
|
||||||
|
|
||||||
def ledlog(value):
|
def ledlog(value):
|
||||||
|
@ -50,98 +47,36 @@ class StairvilleLedPar56(RGB):
|
||||||
dmx.set(self.slot + 6, 255)
|
dmx.set(self.slot + 6, 255)
|
||||||
|
|
||||||
|
|
||||||
class Steady:
|
|
||||||
def __init__(self, r, g, b):
|
|
||||||
self.r = r
|
|
||||||
self.g = g
|
|
||||||
self.b = b
|
|
||||||
|
|
||||||
def update(self, index, count):
|
|
||||||
return (self.r, self.g, self.b)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{type(self).__name__}({self.r}, {self.g}, {self.b})"
|
|
||||||
|
|
||||||
|
|
||||||
class FadeTo(Steady):
|
|
||||||
def __init__(self, r, g, b, t=2.0):
|
|
||||||
super(FadeTo, self).__init__(r, g, b)
|
|
||||||
self.t = t
|
|
||||||
self.start = time()
|
|
||||||
|
|
||||||
def update(self, index, count):
|
|
||||||
h = (time() - self.start) / self.t
|
|
||||||
h = min(h, 1.0)
|
|
||||||
return (int(self.r * h), int(self.g * h), int(self.b * h))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{type(self).__name__}({self.r}, {self.g}, {self.b}, {self.t:.2f})"
|
|
||||||
|
|
||||||
|
|
||||||
class RotatingRainbow:
|
|
||||||
def __init__(self, looptime=10.0):
|
|
||||||
self.looptime = looptime
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update(self, index, count):
|
|
||||||
"""
|
|
||||||
One full round takes self.looptime seconds, each RGB is offset in a circle
|
|
||||||
:param index:
|
|
||||||
:param count:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
hue = (time() / self.looptime + (index + 0.0) / count) % 1.0
|
|
||||||
rgb = hsv_to_rgb(hue, 1, 1)
|
|
||||||
return rgb
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{type(self).__name__}"
|
|
||||||
|
|
||||||
|
|
||||||
class Chase(Steady):
|
|
||||||
def __init__(self, r, g, b, looptime=1.0):
|
|
||||||
super(Chase, self).__init__(r, g, b)
|
|
||||||
self.looptime = looptime
|
|
||||||
|
|
||||||
def update(self, index, count):
|
|
||||||
angle = (time() / self.looptime + (index + 0.0) / count) % 1.0
|
|
||||||
l = 1 - min(abs(angle - 2.0 / count), 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})"
|
|
||||||
|
|
||||||
|
|
||||||
class DMX:
|
class DMX:
|
||||||
def __init__(self, host, port=0x1936, universe=1, maxchan=512):
|
def __init__(self, host, port=0x1936, universe=1, maxchan=512):
|
||||||
self.host = host
|
self._host = host
|
||||||
self.port = port
|
self._port = port
|
||||||
self.universe = universe
|
self._universe = universe
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
|
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
|
||||||
self.socket.setblocking(False)
|
self._socket.setblocking(False)
|
||||||
self.data = bytearray(maxchan)
|
self._data = bytearray(maxchan)
|
||||||
packet = bytearray()
|
packet = bytearray()
|
||||||
packet.extend(map(ord, "Art-Net"))
|
packet.extend(map(ord, "Art-Net"))
|
||||||
packet.append(0x00) # Null terminate Art-Net
|
packet.append(0x00) # Null terminate Art-Net
|
||||||
packet.extend([0x00, 0x50]) # Opcode ArtDMX 0x5000 (Little endian)
|
packet.extend([0x00, 0x50]) # Opcode ArtDMX 0x5000 (Little endian)
|
||||||
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.animation = FadeTo(255, 255, 255)
|
self._color = (0, 0, 0)
|
||||||
self.rgbs = []
|
self._animation = FadeTo((255, 255, 255))
|
||||||
self.thread = None
|
self._rgbs = []
|
||||||
self.updating = False
|
self._thread = None
|
||||||
|
self._updating = False
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self.thread and self.thread.is_alive():
|
if self._thread and self._thread.is_alive():
|
||||||
return
|
return
|
||||||
self.thread = Thread(daemon=True, target=self.background)
|
self._thread = Thread(daemon=True, target=self.background)
|
||||||
self.updating = True
|
self._updating = True
|
||||||
self.thread.start()
|
self._thread.start()
|
||||||
|
|
||||||
def background(self):
|
def background(self):
|
||||||
while self.updating:
|
while self._updating:
|
||||||
self.update()
|
self.update()
|
||||||
# print("updating")
|
# print("updating")
|
||||||
# print(self.data)
|
# print(self.data)
|
||||||
|
@ -149,38 +84,67 @@ class DMX:
|
||||||
sleep(1.0 / 30)
|
sleep(1.0 / 30)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if not self.animation:
|
if not self._animation:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(0, len(self.rgbs)):
|
for i in range(0, len(self._rgbs)):
|
||||||
self.rgbs[i].rgb(self.animation.update(i, len(self.rgbs)))
|
self._rgbs[i].rgb(self._animation.update(i, len(self._rgbs)))
|
||||||
|
|
||||||
packet = self.header[:]
|
packet = self._header[:]
|
||||||
packet.append(self.sequence) # Sequence,
|
packet.append(self._sequence) # Sequence,
|
||||||
packet.append(0x00) # Physical
|
packet.append(0x00) # Physical
|
||||||
packet.append(self.universe & 0xFF) # Universe LowByte
|
packet.append(self._universe & 0xFF) # Universe LowByte
|
||||||
packet.append(self.universe >> 8 & 0xFF) # Universe HighByte
|
packet.append(self._universe >> 8 & 0xFF) # Universe HighByte
|
||||||
|
|
||||||
packet.extend(struct.pack('>h', len(self.data))) # Pack the number of channels Big endian
|
packet.extend(struct.pack('>h', len(self._data))) # Pack the number of channels Big endian
|
||||||
packet.extend(self.data)
|
packet.extend(self._data)
|
||||||
self.socket.sendto(packet, (self.host, self.port))
|
self._socket.sendto(packet, (self._host, self._port))
|
||||||
# print(f"sent {len(packet)} bytes, {threading.get_native_id()}")
|
# print(f"sent {len(packet)} bytes, {threading.get_native_id()}")
|
||||||
|
|
||||||
self.sequence += 1
|
self._sequence += 1
|
||||||
if self.sequence > 255:
|
if self._sequence > 255:
|
||||||
self.sequence = 1
|
self._sequence = 1
|
||||||
|
|
||||||
def set(self, slot, value):
|
def set(self, slot, value):
|
||||||
self.data[slot - 1] = value
|
self._data[slot - 1] = value
|
||||||
|
|
||||||
def setAnimation(self, animation):
|
@property
|
||||||
if not animation:
|
def animation(self) -> str:
|
||||||
self.updating = False
|
return self._animation.name()
|
||||||
self.thread.join()
|
|
||||||
|
@animation.setter
|
||||||
|
def animation(self, animation: Union[Animation, str]):
|
||||||
|
if isinstance(animation, str):
|
||||||
|
if animation == "off":
|
||||||
|
animation = Off()
|
||||||
|
elif animation == "chase":
|
||||||
|
animation = Chase(self._color)
|
||||||
|
elif animation == "fade":
|
||||||
|
animation = FadeTo(self._color)
|
||||||
|
elif animation == "rainbow":
|
||||||
|
animation = RotatingRainbow()
|
||||||
|
elif animation == "steady":
|
||||||
|
animation = Steady(self._color)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"No such animation {animation}")
|
||||||
|
self._animation = animation
|
||||||
|
if isinstance(animation, Off):
|
||||||
|
self._updating = False
|
||||||
|
if self._thread and self._thread.is_alive():
|
||||||
|
self._thread.join()
|
||||||
# one frame black
|
# one frame black
|
||||||
self.animation = Steady(0, 0, 0)
|
self._animation = Steady((0, 0, 0))
|
||||||
self.update()
|
self.update()
|
||||||
else:
|
else:
|
||||||
self.animation = animation
|
|
||||||
self.start()
|
self.start()
|
||||||
print(f"Animation: {animation}", file=sys.stderr)
|
print(f"Animation: {animation}", file=sys.stderr)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color(self):
|
||||||
|
return self._color
|
||||||
|
|
||||||
|
@color.setter
|
||||||
|
def color(self, color):
|
||||||
|
if self._color != color:
|
||||||
|
self._color = color
|
||||||
|
self.animation = self.animation
|
||||||
|
|
56
foobaz.py
56
foobaz.py
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
from bottle import route, run, static_file, view
|
from bottle import post, request, route, run, static_file, view
|
||||||
|
|
||||||
from dmx import DMX, Bar252, StairvilleLedPar56, Steady, RotatingRainbow, REDSpot18RGB, Chase, FadeTo
|
from animation import Off
|
||||||
|
from dmx import DMX, Bar252, StairvilleLedPar56, REDSpot18RGB
|
||||||
|
|
||||||
|
|
||||||
@route('/')
|
@route('/')
|
||||||
|
@ -17,23 +19,28 @@ def index():
|
||||||
def static(path):
|
def static(path):
|
||||||
return static_file(path, root='static')
|
return static_file(path, root='static')
|
||||||
|
|
||||||
@route('/api/state/<state>')
|
@route('/api/state')
|
||||||
def update(state):
|
def state():
|
||||||
if state == "off":
|
return {
|
||||||
dmx.setAnimation(None)
|
'animation': dmx.animation,
|
||||||
elif state == "white":
|
'color': rgb_to_hex(dmx.color)
|
||||||
dmx.setAnimation(Steady(255, 255, 255))
|
}
|
||||||
elif state == "red":
|
|
||||||
dmx.setAnimation(Steady(255, 0, 0))
|
|
||||||
elif state == "blue":
|
|
||||||
dmx.setAnimation(Steady(0, 0, 255))
|
|
||||||
elif state == "rainbow":
|
|
||||||
dmx.setAnimation(RotatingRainbow())
|
|
||||||
elif state == "chase-blue":
|
|
||||||
dmx.setAnimation(Chase(0, 0, 255))
|
|
||||||
dmx.start()
|
|
||||||
return {'result': 'ok'}
|
|
||||||
|
|
||||||
|
@post('/api/state')
|
||||||
|
def update():
|
||||||
|
json = request.json
|
||||||
|
print(json)
|
||||||
|
dmx.animation = json["animation"]
|
||||||
|
dmx.color = hex_to_rgb(json["color"])
|
||||||
|
return state()
|
||||||
|
|
||||||
|
|
||||||
|
def hex_to_rgb(hex: str) -> tuple[int, ...]:
|
||||||
|
return tuple(int(hex[i:i+2], 16) for i in (1, 3, 5))
|
||||||
|
|
||||||
|
|
||||||
|
def rgb_to_hex(color: Tuple[int, int, int]) -> str:
|
||||||
|
return f"#{color[0]:02X}{color[1]:02X}{color[2]:02X}"
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
global dmx
|
global dmx
|
||||||
|
@ -47,17 +54,17 @@ def main(args):
|
||||||
|
|
||||||
print(f"Starting DMX via Art-Net to {artnet}", file=sys.stderr)
|
print(f"Starting DMX via Art-Net to {artnet}", file=sys.stderr)
|
||||||
# dmx = DMX("10.31.242.35")
|
# dmx = DMX("10.31.242.35")
|
||||||
dmx = DMX(artnet, maxchan=64)
|
dmx = DMX(artnet, maxchan=128)
|
||||||
|
|
||||||
if args.room == "shop":
|
if args.room == "shop":
|
||||||
dmx.rgbs = [
|
dmx._rgbs = [
|
||||||
REDSpot18RGB(dmx, 1),
|
REDSpot18RGB(dmx, 1),
|
||||||
REDSpot18RGB(dmx, 5),
|
REDSpot18RGB(dmx, 5),
|
||||||
REDSpot18RGB(dmx, 9),
|
REDSpot18RGB(dmx, 9),
|
||||||
REDSpot18RGB(dmx, 13),
|
REDSpot18RGB(dmx, 13),
|
||||||
]
|
]
|
||||||
elif args.room == "big":
|
elif args.room == "big":
|
||||||
dmx.rgbs = [
|
dmx._rgbs = [
|
||||||
StairvilleLedPar56(dmx, 1),
|
StairvilleLedPar56(dmx, 1),
|
||||||
StairvilleLedPar56(dmx, 8),
|
StairvilleLedPar56(dmx, 8),
|
||||||
StairvilleLedPar56(dmx, 15),
|
StairvilleLedPar56(dmx, 15),
|
||||||
|
@ -70,10 +77,11 @@ def main(args):
|
||||||
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)
|
||||||
dmx.setAnimation(FadeTo(128, 128, 128))
|
dmx.animation = Off()
|
||||||
dmx.start()
|
dmx.color = (128, 128, 128)
|
||||||
|
dmx.animation = "fade"
|
||||||
|
|
||||||
run(host='0.0.0.0', port=8080, reloader=False)
|
run(host='0.0.0.0', port=8080, reloader=False, debug=True)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
|
@ -2,3 +2,55 @@ body {
|
||||||
background: black;
|
background: black;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
width: 40em;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.button_color {
|
||||||
|
width: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_white {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_red {
|
||||||
|
background-color: #ff0000;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_yellow {
|
||||||
|
background-color: #ffff00;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_green {
|
||||||
|
background-color: #00ff00;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_cyan {
|
||||||
|
background-color: #00ffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_blue {
|
||||||
|
background-color: #0000ff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_color_magenta {
|
||||||
|
background-color: #ff00ff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,64 @@
|
||||||
function setstate(state) {
|
(function() {
|
||||||
var request = new XMLHttpRequest();
|
let state = {
|
||||||
request.onreadystatechange = function() {
|
'animation': 'off',
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
'color': "#000000"
|
||||||
// process state here
|
}
|
||||||
console.log("state changed to " + state);
|
|
||||||
}
|
let setState = function() {
|
||||||
};
|
let request = new XMLHttpRequest();
|
||||||
request.open("GET", "/api/state/" + state, true);
|
request.onreadystatechange = function() {
|
||||||
request.send();
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
}
|
// process state here
|
||||||
|
console.log("state changed to " + JSON.stringify(state));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.open("POST", "/api/state", true);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
request.send(JSON.stringify(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
let animation_radios = document.getElementsByClassName("js_animation")
|
||||||
|
for (let el of animation_radios) {
|
||||||
|
el.addEventListener("click", function(ev) {
|
||||||
|
state.animation = el.value;
|
||||||
|
setState();
|
||||||
|
})
|
||||||
|
console.log("attached to " + el.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = document.getElementById("color")
|
||||||
|
color.addEventListener("change", function(ev) {
|
||||||
|
state.color = color.value;
|
||||||
|
setState();
|
||||||
|
})
|
||||||
|
for (let el of document.getElementsByClassName("js_color")) {
|
||||||
|
el.addEventListener("click", function(ev) {
|
||||||
|
color.value = el.dataset.color;
|
||||||
|
state.color = color.value;
|
||||||
|
setState();
|
||||||
|
})
|
||||||
|
console.log("attached to " + el.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setInterval(function() {
|
||||||
|
// console.log("Updating state")
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.onreadystatechange = function(response) {
|
||||||
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
|
var json = JSON.parse(this.responseText)
|
||||||
|
// console.log("state is " + JSON.stringify(json));
|
||||||
|
color.value = json.color;
|
||||||
|
state.color = json.color;
|
||||||
|
if (state.animation != json.animation) {
|
||||||
|
console.log("Animation has been updated to " + json.animation)
|
||||||
|
state.animation = json.animation;
|
||||||
|
for (let el of animation_radios) {
|
||||||
|
el.checked = el.value == state.animation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.open("GET", "/api/state", true);
|
||||||
|
request.send();
|
||||||
|
}, 500);
|
||||||
|
})();
|
||||||
|
|
|
@ -2,36 +2,44 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Foo Baz DMX</title>
|
<title>Foo Baz DMX</title>
|
||||||
<link rel="stylesheet" href="static/main.css">
|
<link rel="stylesheet" href="static/main.css">
|
||||||
<script src="static/main.js"></script>
|
<script src="static/main.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Foo Baz DMX</h1>
|
<h1>Foo Baz DMX</h1>
|
||||||
<div class="buttons">
|
<div class="controls">
|
||||||
<fieldset>
|
<fieldset class="buttons">
|
||||||
<legend>Lights:</legend>
|
<legend>Animation</legend>
|
||||||
<div>
|
<div>
|
||||||
<input type="radio" name="state" id="state_off" value="off" onclick="setstate('off')"/>
|
<input type="radio" class="js_animation" name="state" id="animation_off" value="off"/>
|
||||||
<label for="state_off">Off</label>
|
<label for="animation_off">Off</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="radio" name="state" id="state_white" value="white" onclick="setstate('white')"/>
|
<input type="radio" class="js_animation" name="state" id="animation_white" value="steady"/>
|
||||||
<label for="state_white">White</label>
|
<label for="animation_white">Steady</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="radio" name="state" id="state_red" value="red" onclick="setstate('red')"/>
|
<input type="radio" class="js_animation" name="state" id="animation_chase" value="chase"/>
|
||||||
<label for="state_red">Red</label>
|
<label for="animation_chase">Chase</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="radio" name="state" id="state_blue" value="red" onclick="setstate('blue')"/>
|
<input type="radio" class="js_animation" name="state" id="animation_rainbow" value="rainbow"/>
|
||||||
<label for="state_blue">Blue</label>
|
<label for="animation_rainbow">Rainbow</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="colors">
|
||||||
|
<legend>Color</legend>
|
||||||
|
<div>
|
||||||
|
<input type="color" name="color" id="color" value="#0000ff"/>
|
||||||
|
<label for="color">Color</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="radio" name="state" id="state_rainbow" value="rainbow" onclick="setstate('rainbow')"/>
|
<input type="button" class="button_color button_color_white js_color" name="white" value="White" data-color="#ffffff"/>
|
||||||
<label for="state_rainbow">Rainbow</label>
|
<input type="button" class="button_color button_color_red js_color" name="red" value="Red" data-color="#ff0000"/>
|
||||||
</div>
|
<input type="button" class="button_color button_color_yellow js_color" name="yellow" value="Yellow" data-color="#ffff00"/>
|
||||||
<div>
|
<input type="button" class="button_color button_color_green js_color" name="green" value="Green" data-color="#00ff00"/>
|
||||||
<input type="radio" name="state" id="state_chase_blue" value="chase-blue" onclick="setstate('chase-blue')"/>
|
<input type="button" class="button_color button_color_cyan js_color" name="cyan" value="Cyan" data-color="#00ffff"/>
|
||||||
<label for="state_chase_blue">Blue Chase</label>
|
<input type="button" class="button_color button_color_blue js_color" name="blue" value="Blue" data-color="#0000ff"/>
|
||||||
|
<input type="button" class="button_color button_color_magenta js_color" name="magenta" value="Magenta" data-color="#ff00ff"/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue