Erste Versuche

This commit is contained in:
Stefan Bethke 2022-07-20 22:27:40 +02:00
commit b7586bed26
8 changed files with 253 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__pycache__

120
dmx.py Normal file
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
body {
background: black;
color: white;
}

11
static/main.js Normal file
View 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
View 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>