From b7586bed2621353e12828203ea30e34f2902d205 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Wed, 20 Jul 2022 22:27:40 +0200 Subject: [PATCH] Erste Versuche --- .gitignore | 1 + dmx.py | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ foobaz.py | 49 ++++++++++++++++++++ poetry.lock | 18 ++++++++ pyproject.toml | 15 ++++++ static/main.css | 4 ++ static/main.js | 11 +++++ views/index.tpl | 35 ++++++++++++++ 8 files changed, 253 insertions(+) create mode 100644 .gitignore create mode 100644 dmx.py create mode 100644 foobaz.py create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 static/main.css create mode 100644 static/main.js create mode 100644 views/index.tpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/dmx.py b/dmx.py new file mode 100644 index 0000000..5723a62 --- /dev/null +++ b/dmx.py @@ -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}") \ No newline at end of file diff --git a/foobaz.py b/foobaz.py new file mode 100644 index 0000000..19782ea --- /dev/null +++ b/foobaz.py @@ -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/') +def static(path): + return static_file(path, root='static') + +@route('/api/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) + diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..2a815ad --- /dev/null +++ b/poetry.lock @@ -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"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..548c66e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "foobazdmx" +version = "0.1.0" +description = "" +authors = ["Stefan Bethke "] + +[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" diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..e3c32fb --- /dev/null +++ b/static/main.css @@ -0,0 +1,4 @@ +body { + background: black; + color: white; +} \ No newline at end of file diff --git a/static/main.js b/static/main.js new file mode 100644 index 0000000..2fbbf47 --- /dev/null +++ b/static/main.js @@ -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(); +} \ No newline at end of file diff --git a/views/index.tpl b/views/index.tpl new file mode 100644 index 0000000..d96a250 --- /dev/null +++ b/views/index.tpl @@ -0,0 +1,35 @@ + + + Foo Baz DMX + + + + +

Foo Baz DMX

+
+
+ Lights: +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + \ No newline at end of file