Erste Versuche
This commit is contained in:
commit
b7586bed26
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
__pycache__
|
120
dmx.py
Normal file
120
dmx.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import colorsys
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
from threading import Thread
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
|
||||||
|
class RGB:
|
||||||
|
def __init__(self, dmx, slot, offset=0):
|
||||||
|
self.dmx = dmx
|
||||||
|
self.slot = slot
|
||||||
|
self.offset = offset
|
||||||
|
|
||||||
|
def rgb(self, color):
|
||||||
|
(r, g, b) = color
|
||||||
|
self.dmx.set(self.slot+self.offset+0, r)
|
||||||
|
self.dmx.set(self.slot+self.offset+1, g)
|
||||||
|
self.dmx.set(self.slot+self.offset+2, b)
|
||||||
|
|
||||||
|
class Bar252(RGB):
|
||||||
|
def __init__(self, dmx, slot=1):
|
||||||
|
super(Bar252, self).__init__(dmx, slot, 2)
|
||||||
|
dmx.set(self.slot+0, 81)
|
||||||
|
dmx.set(self.slot+1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class StairvilleLedPar56(RGB):
|
||||||
|
def __init__(self, dmx, slot=1):
|
||||||
|
super(StairvilleLedPar56, self).__init__(dmx, slot, 0)
|
||||||
|
dmx.set(self.slot+3, 0)
|
||||||
|
dmx.set(self.slot+4, 0)
|
||||||
|
dmx.set(self.slot+5, 0)
|
||||||
|
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"steady({self.r}, {self.g}, {self.b})"
|
||||||
|
|
||||||
|
class RotatingRainbow:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, index, count):
|
||||||
|
"""
|
||||||
|
One full round takes 10 seconds, each RGB is offset in a circle
|
||||||
|
:param index:
|
||||||
|
:param count:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
hue = (time() / 10.0 + (index + 0.0) / count) % 1.0
|
||||||
|
rgb = self.hsv_to_rgb(hue, 1, 1)
|
||||||
|
return rgb
|
||||||
|
|
||||||
|
def hsv_to_rgb(self, h, s, v):
|
||||||
|
(r, g, b) = colorsys.hsv_to_rgb(h, s, v)
|
||||||
|
return [int(r * 255), int(g * 255), int(b * 255)]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "RotatingRainbow"
|
||||||
|
|
||||||
|
class DMX:
|
||||||
|
def __init__(self, host, port=0x1936, universe=1):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.universe = universe
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
|
||||||
|
self.socket.setblocking(False)
|
||||||
|
self.data = bytearray(512)
|
||||||
|
packet = bytearray()
|
||||||
|
packet.extend(map(ord, "Art-Net"))
|
||||||
|
packet.append(0x00) # Null terminate Art-Net
|
||||||
|
packet.extend([0x00, 0x50]) # Opcode ArtDMX 0x5000 (Little endian)
|
||||||
|
packet.extend([0x00, 0x0e]) # Protocol version 14
|
||||||
|
self.header = packet
|
||||||
|
self.animation = Steady(64, 64, 64)
|
||||||
|
self.rgbs = []
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.thread = Thread(daemon=True, target=self.background)
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
def background(self):
|
||||||
|
print("background starts")
|
||||||
|
while True:
|
||||||
|
animation = self.animation
|
||||||
|
for i in range(0, len(self.rgbs)):
|
||||||
|
self.rgbs[i].rgb(animation.update(i, len(self.rgbs)))
|
||||||
|
self.update()
|
||||||
|
# print("updating")
|
||||||
|
print(self.data)
|
||||||
|
break
|
||||||
|
sleep(1.0/30)
|
||||||
|
print("background ends")
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
packet = self.header[:]
|
||||||
|
packet.append(0) # Sequence,
|
||||||
|
packet.append(0x00) # Physical
|
||||||
|
packet.append(self.universe & 0xFF) # Universe LowByte
|
||||||
|
packet.append(self.universe >> 8 & 0xFF) # Universe HighByte
|
||||||
|
|
||||||
|
packet.extend(struct.pack('>h', 512)) # Pack the number of channels Big endian
|
||||||
|
packet.extend(self.data)
|
||||||
|
self.socket.sendto(packet, (self.host, self.port))
|
||||||
|
|
||||||
|
def set(self, slot, value):
|
||||||
|
self.data[slot-1] = value
|
||||||
|
|
||||||
|
def setAnimation(self, animation):
|
||||||
|
self.animation = animation
|
||||||
|
print(f"Animation: {animation}")
|
49
foobaz.py
Normal file
49
foobaz.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from bottle import route, run, static_file, template, view
|
||||||
|
|
||||||
|
from dmx import DMX, Bar252, StairvilleLedPar56, Steady, RotatingRainbow
|
||||||
|
|
||||||
|
|
||||||
|
@route('/')
|
||||||
|
@view('index')
|
||||||
|
def index():
|
||||||
|
return {'foo': 'bar'}
|
||||||
|
|
||||||
|
@route('/static/<path:path>')
|
||||||
|
def static(path):
|
||||||
|
return static_file(path, root='static')
|
||||||
|
|
||||||
|
@route('/api/state/<state>')
|
||||||
|
def update(state):
|
||||||
|
print(f"state -> {state}")
|
||||||
|
if state == "off":
|
||||||
|
dmx.setAnimation(Steady(0, 0, 0))
|
||||||
|
elif state == "white":
|
||||||
|
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())
|
||||||
|
dmx.start()
|
||||||
|
return {'result': 'ok'}
|
||||||
|
|
||||||
|
dmx = DMX("10.31.242.35")
|
||||||
|
|
||||||
|
dmx.rgbs = [
|
||||||
|
StairvilleLedPar56(dmx, 1),
|
||||||
|
StairvilleLedPar56(dmx, 8),
|
||||||
|
StairvilleLedPar56(dmx, 15),
|
||||||
|
StairvilleLedPar56(dmx, 22),
|
||||||
|
Bar252(dmx, 29),
|
||||||
|
Bar252(dmx, 40),
|
||||||
|
Bar252(dmx, 51),
|
||||||
|
Bar252(dmx, 62),
|
||||||
|
]
|
||||||
|
dmx.update()
|
||||||
|
dmx.start()
|
||||||
|
|
||||||
|
run(host='localhost', port=8080, reloader=True)
|
||||||
|
|
18
poetry.lock
generated
Normal file
18
poetry.lock
generated
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[[package]]
|
||||||
|
name = "bottle"
|
||||||
|
version = "0.12.21"
|
||||||
|
description = "Fast and simple WSGI-framework for small web-applications."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "1.1"
|
||||||
|
python-versions = "^3.9"
|
||||||
|
content-hash = "7884484664662ef3a2683e8032b957139e6bb0f977eb2755f300188d91168ca9"
|
||||||
|
|
||||||
|
[metadata.files]
|
||||||
|
bottle = [
|
||||||
|
{file = "bottle-0.12.21-py3-none-any.whl", hash = "sha256:6e1c9817019dae3a8c20adacaf09035251798d2ae2fcc8ce43157ee72965f257"},
|
||||||
|
{file = "bottle-0.12.21.tar.gz", hash = "sha256:787c61b6cc02b9c229bf2663011fac53dd8fc197f7f8ad2eeede29d888d7887e"},
|
||||||
|
]
|
15
pyproject.toml
Normal file
15
pyproject.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "foobazdmx"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Stefan Bethke <stb@lassitu.de>"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.10"
|
||||||
|
bottle = "^0.12.21"
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
4
static/main.css
Normal file
4
static/main.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
body {
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
}
|
11
static/main.js
Normal file
11
static/main.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
function setstate(state) {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
|
// process state here
|
||||||
|
console.log("state changed to " + state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.open("GET", "/api/state/" + state, true);
|
||||||
|
request.send();
|
||||||
|
}
|
35
views/index.tpl
Normal file
35
views/index.tpl
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Foo Baz DMX</title>
|
||||||
|
<link rel="stylesheet" href="static/main.css">
|
||||||
|
<script src="static/main.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Foo Baz DMX</h1>
|
||||||
|
<div class="buttons">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Lights:</legend>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="state" id="state_off" value="off" onclick="setstate('off')"/>
|
||||||
|
<label for="state_off">Off</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="state" id="state_white" value="white" onclick="setstate('white')"/>
|
||||||
|
<label for="state_white">White</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="state" id="state_red" value="red" onclick="setstate('red')"/>
|
||||||
|
<label for="state_red">Red</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="state" id="state_blue" value="red" onclick="setstate('blue')"/>
|
||||||
|
<label for="state_blue">Blue</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="state" id="state_rainbow" value="rainbow" onclick="setstate('rainbow')"/>
|
||||||
|
<label for="state_rainbow">Rainbow</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue