Add Hackertours room and animation #4

Merged
stb merged 9 commits from hackertours into main 2024-12-17 12:15:13 +01:00
7 changed files with 80 additions and 16 deletions

View file

@ -120,6 +120,7 @@ class TwoColor(Steady):
def name(self): def name(self):
return str(self) return str(self)
class Caramelldansen(Steady): class Caramelldansen(Steady):
""" """
by Max & Lightmoll (https://lght.ml) by Max & Lightmoll (https://lght.ml)
@ -233,9 +234,34 @@ class Chase(Steady):
return "chase" return "chase"
class ChaseRandom(Animation): class Hackertours(Steady):
"""
Base color yellow, with green wandering back and forth
"""
def __init__(self, color, base=(255,223,0), looptime=4.0):
super(Hackertours, self).__init__(color)
self.looptime = looptime
self.base = base
def update(self, index, count):
# angle is the position of the highlight on a circle, range [0, 1]
angle = (time() / self.looptime + (index + 0.0) / count) % 1.0
l = 1 - min(abs(angle - 1 / count) * .9, 1.0 / count) * count
return (
int(self.r * l + self.base[0] * (1-l)),
int(self.g * l + self.base[1] * (1-l)),
int(self.b * l + self.base[2] * (1-l)))
def __str__(self):
return f"{type(self).__name__}({self.r}, {self.g}, {self.b}, {self.looptime:.2f})"
def name(self):
return "hackertours"
class ChaseRandom(Steady):
def __init__(self, color, looptime=1.0): def __init__(self, color, looptime=1.0):
super(Chase, self).__init__(color) super(ChaseRandom, self).__init__(color)
self.looptime = looptime self.looptime = looptime
def update(self, index, count): def update(self, index, count):

16
dmx.py
View file

@ -5,7 +5,8 @@ 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, RandomSingle, Steady, FadeTo, RotatingRainbow, Chase, TwoColor, Caramelldansen from animation import Animation, Off, RandomSingle, Steady, FadeTo, RotatingRainbow, Chase, TwoColor, Caramelldansen, \
Hackertours
def ledlog(value): def ledlog(value):
@ -47,6 +48,15 @@ class StairvilleLedPar56(RGB):
dmx.set(self.slot + 6, 255) dmx.set(self.slot + 6, 255)
class ZhennbyPar(RGB):
def __init__(self, dmx, slot=1):
super(ZhennbyPar, self).__init__(dmx, slot, 1)
dmx.set(self.slot + 0, 255)
dmx.set(self.slot + 4, 0)
dmx.set(self.slot + 5, 0)
dmx.set(self.slot + 6, 0)
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
@ -131,6 +141,8 @@ class DMX:
animation = Caramelldansen(self._color) animation = Caramelldansen(self._color)
elif animation == "randomsingle": elif animation == "randomsingle":
animation = RandomSingle(self._color) animation = RandomSingle(self._color)
elif animation == "hackertours":
animation = Hackertours(self._color)
else: else:
raise ValueError(f"No such animation {animation}") raise ValueError(f"No such animation {animation}")
self._animation = animation self._animation = animation
@ -153,5 +165,5 @@ class DMX:
@color.setter @color.setter
def color(self, color): def color(self, color):
if self._color != color: if self._color != color:
self._color = color self._color = [int(c) for c in color]
self.animation = self.animation self.animation = self.animation

View file

@ -7,7 +7,7 @@ from typing import Tuple
from bottle import post, request, route, run, static_file, view 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, ZhennbyPar
room = '' room = ''
@ -51,6 +51,8 @@ def main(args):
parser.add_argument('-l', '--listen', type=int, required=False, default=8080, help="TCP port to listen on for web") 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") parser.add_argument('-u', '--universe', type=int, required=False, default=1, help="Universe to send to")
parser.add_argument('-A', '--animation', type=str, required=False, default="off", help="Initial animation")
parser.add_argument('-C', '--color', type=str, required=False, default="255,255,0", help="Initial color")
args = parser.parse_args(args) args = parser.parse_args(args)
print(f"Starting DMX via Art-Net to {args.artnet}", file=sys.stderr) print(f"Starting DMX via Art-Net to {args.artnet}", file=sys.stderr)
@ -74,13 +76,20 @@ def main(args):
Bar252(dmx, 51), Bar252(dmx, 51),
Bar252(dmx, 62), Bar252(dmx, 62),
] ]
elif args.room == "hackertours":
dmx._rgbs = [
ZhennbyPar(dmx, 1),
ZhennbyPar(dmx, 8),
ZhennbyPar(dmx, 15),
ZhennbyPar(dmx, 22),
]
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 room = args.room
dmx.animation = Off() dmx.animation = Off()
dmx.color = (0, 0, 0) dmx.color = args.color.split(',')
dmx.animation = "off" dmx.animation = args.animation
run(host='0.0.0.0', port=args.listen, reloader=False, debug=True) run(host='0.0.0.0', port=args.listen, reloader=False, debug=True)

13
hackertours.service Normal file
View file

@ -0,0 +1,13 @@
[Unit]
Description=Run foobazdmx controller in Hackertours config
[Service]
Type=simple
ExecStart=poetry run python ./foobaz.py -a 127.0.0.1 -r hackertours -A hackertours -C 0,255,32
WorkingDirectory=%h/working/foobazdmx
Restart=on-failure
[Install]
WantedBy=default.target
# link or copy me to ~/.config/systemd/user

17
poetry.lock generated
View file

@ -1,18 +1,17 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]] [[package]]
name = "bottle" name = "bottle"
version = "0.12.21" version = "0.12.25"
description = "Fast and simple WSGI-framework for small web-applications." description = "Fast and simple WSGI-framework for small web-applications."
category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [
{file = "bottle-0.12.25-py3-none-any.whl", hash = "sha256:d6f15f9d422670b7c073d63bd8d287b135388da187a0f3e3c19293626ce034ea"},
{file = "bottle-0.12.25.tar.gz", hash = "sha256:e1a9c94970ae6d710b3fb4526294dfeb86f2cb4a81eff3a4b98dc40fb0e5e021"},
]
[metadata] [metadata]
lock-version = "1.1" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "861e3ad9d0b00deb876d2ca7797a608f215c7b91ec9b38f367b24bd096a28478" content-hash = "861e3ad9d0b00deb876d2ca7797a608f215c7b91ec9b38f367b24bd096a28478"
[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"},
]

View file

@ -3,6 +3,7 @@ name = "foobazdmx"
version = "0.1.0" version = "0.1.0"
description = "" description = ""
authors = ["Stefan Bethke <stb@lassitu.de>"] authors = ["Stefan Bethke <stb@lassitu.de>"]
#package-mode = false
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"

View file

@ -21,6 +21,10 @@
<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>
</div> </div>
<div>
<input type="radio" class="js_animation" name="state" id="animation_hackertours" value="hackertours"/>
<label for="animation_hackertours">Hackertours</label>
</div>
</fieldset> </fieldset>
<fieldset class="colors"> <fieldset class="colors">
<legend>Color</legend> <legend>Color</legend>