From 93b409736f02b0d13a54f23e43abbecaa05cdf31 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Mon, 16 Dec 2024 16:15:56 +0100 Subject: [PATCH 1/9] Implement Hackertours effect --- animation.py | 32 ++++++++++++++++++++++++++++++-- dmx.py | 9 +++++++++ foobaz.py | 9 ++++++++- poetry.lock | 17 ++++++++--------- pyproject.toml | 1 + views/index.tpl | 4 ++++ 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/animation.py b/animation.py index 64f7fb0..28abb32 100644 --- a/animation.py +++ b/animation.py @@ -120,6 +120,7 @@ class TwoColor(Steady): def name(self): return str(self) + class Caramelldansen(Steady): """ by Max & Lightmoll (https://lght.ml) @@ -233,9 +234,36 @@ class Chase(Steady): return "chase" -class ChaseRandom(Animation): +class Hackertours(Steady): + """ + Base color yellow, with green wandering back and forth + """ + def __init__(self, color, base=(255,255,0), looptime=2.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 + # map 0->1, 0.5->0, 1->1 to convert from circle to cylon + angle = abs(angle * 2 - 1) + 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): - super(Chase, self).__init__(color) + super(ChaseRandom, self).__init__(color) self.looptime = looptime def update(self, index, count): diff --git a/dmx.py b/dmx.py index cc954de..e1c0410 100644 --- a/dmx.py +++ b/dmx.py @@ -47,6 +47,15 @@ class StairvilleLedPar56(RGB): 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, 0) + dmx.set(self.slot + 4, 0) + dmx.set(self.slot + 5, 0) + dmx.set(self.slot + 6, 0) + + class DMX: def __init__(self, host, port=0x1936, universe=1, maxchan=512): self._host = host diff --git a/foobaz.py b/foobaz.py index 64f02d4..0db4db2 100644 --- a/foobaz.py +++ b/foobaz.py @@ -7,7 +7,7 @@ from typing import Tuple from bottle import post, request, route, run, static_file, view from animation import Off -from dmx import DMX, Bar252, StairvilleLedPar56, REDSpot18RGB +from dmx import DMX, Bar252, StairvilleLedPar56, REDSpot18RGB, ZhennbyPar room = '' @@ -74,6 +74,13 @@ def main(args): Bar252(dmx, 51), Bar252(dmx, 62), ] + elif args.room == "hackertours": + dmx._rgbs = [ + ZhennbyPar(dmx, 1), + ZhennbyPar(dmx, 8), + ZhennbyPar(dmx, 15), + ZhennbyPar(dmx, 22), + ] else: print(f"Unknown room {args.room}", file=sys.stderr) sys.exit(64) diff --git a/poetry.lock b/poetry.lock index 38d10b1..0fdf274 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,18 +1,17 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + [[package]] name = "bottle" -version = "0.12.21" +version = "0.12.25" description = "Fast and simple WSGI-framework for small web-applications." -category = "main" optional = false 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] -lock-version = "1.1" +lock-version = "2.0" python-versions = "^3.9" 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"}, -] diff --git a/pyproject.toml b/pyproject.toml index 038b5da..f33df46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ name = "foobazdmx" version = "0.1.0" description = "" authors = ["Stefan Bethke "] +package-mode = false [tool.poetry.dependencies] python = "^3.9" diff --git a/views/index.tpl b/views/index.tpl index 580768b..1953385 100644 --- a/views/index.tpl +++ b/views/index.tpl @@ -21,6 +21,10 @@ +
+ + +
Color From 4b23f0a2bfee5768a89ac6c40fe9d4fef8413c6f Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Mon, 16 Dec 2024 16:19:35 +0100 Subject: [PATCH 2/9] Not yet on Debian --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f33df46..01bb425 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "foobazdmx" version = "0.1.0" description = "" authors = ["Stefan Bethke "] -package-mode = false +#package-mode = false [tool.poetry.dependencies] python = "^3.9" From b904e3514b67c2519a0a49739e3819719bf7ba26 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Mon, 16 Dec 2024 16:31:26 +0100 Subject: [PATCH 3/9] No dimming instead of full --- dmx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmx.py b/dmx.py index e1c0410..b34a850 100644 --- a/dmx.py +++ b/dmx.py @@ -50,7 +50,7 @@ class StairvilleLedPar56(RGB): class ZhennbyPar(RGB): def __init__(self, dmx, slot=1): super(ZhennbyPar, self).__init__(dmx, slot, 1) - dmx.set(self.slot + 0, 0) + dmx.set(self.slot + 0, 255) dmx.set(self.slot + 4, 0) dmx.set(self.slot + 5, 0) dmx.set(self.slot + 6, 0) From 60ffeefd6aec5e02e50dfa1c17362e1b15d2c11e Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Mon, 16 Dec 2024 16:33:08 +0100 Subject: [PATCH 4/9] Add Hackertours to list of animations --- dmx.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dmx.py b/dmx.py index b34a850..d313f11 100644 --- a/dmx.py +++ b/dmx.py @@ -5,7 +5,8 @@ from threading import Thread from time import sleep 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): @@ -140,6 +141,8 @@ class DMX: animation = Caramelldansen(self._color) elif animation == "randomsingle": animation = RandomSingle(self._color) + elif animation == "hackertours": + animation = Hackertours(self._color) else: raise ValueError(f"No such animation {animation}") self._animation = animation From 10b89e784f192c9482a1a048dd4ed57c9f6041a9 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Mon, 16 Dec 2024 17:50:30 +0100 Subject: [PATCH 5/9] Rotation only for now --- animation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/animation.py b/animation.py index 28abb32..0844503 100644 --- a/animation.py +++ b/animation.py @@ -238,7 +238,7 @@ class Hackertours(Steady): """ Base color yellow, with green wandering back and forth """ - def __init__(self, color, base=(255,255,0), looptime=2.0): + def __init__(self, color, base=(255,255,0), looptime=4.0): super(Hackertours, self).__init__(color) self.looptime = looptime self.base = base @@ -246,8 +246,6 @@ class Hackertours(Steady): 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 - # map 0->1, 0.5->0, 1->1 to convert from circle to cylon - angle = abs(angle * 2 - 1) l = 1 - min(abs(angle - 1 / count) * .9, 1.0 / count) * count return ( int(self.r * l + self.base[0] * (1-l)), From 32bc2660d40cdc7f7153cbe49d7192afd4da9b5b Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 17 Dec 2024 09:34:20 +0100 Subject: [PATCH 6/9] Make default background color into a warmer green --- animation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/animation.py b/animation.py index 0844503..86d5d23 100644 --- a/animation.py +++ b/animation.py @@ -238,7 +238,7 @@ class Hackertours(Steady): """ Base color yellow, with green wandering back and forth """ - def __init__(self, color, base=(255,255,0), looptime=4.0): + def __init__(self, color, base=(255,223,0), looptime=4.0): super(Hackertours, self).__init__(color) self.looptime = looptime self.base = base From 52c1cf5c11ebe1443d9b0e52109833ab406a8ccf Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 17 Dec 2024 09:34:41 +0100 Subject: [PATCH 7/9] Allow initial animation and color to be set on the cmd line --- dmx.py | 2 +- foobaz.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dmx.py b/dmx.py index d313f11..453d116 100644 --- a/dmx.py +++ b/dmx.py @@ -165,5 +165,5 @@ class DMX: @color.setter def color(self, color): if self._color != color: - self._color = color + self._color = [int(c) for c in color] self.animation = self.animation diff --git a/foobaz.py b/foobaz.py index 0db4db2..7e0812b 100644 --- a/foobaz.py +++ b/foobaz.py @@ -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('-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('-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) print(f"Starting DMX via Art-Net to {args.artnet}", file=sys.stderr) @@ -86,8 +88,8 @@ def main(args): sys.exit(64) room = args.room dmx.animation = Off() - dmx.color = (0, 0, 0) - dmx.animation = "off" + dmx.color = args.color.split(',') + dmx.animation = args.animation run(host='0.0.0.0', port=args.listen, reloader=False, debug=True) From 104333b1f5fce3bedb60a101f6420b3d53a3e63f Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 17 Dec 2024 09:42:45 +0100 Subject: [PATCH 8/9] Add a use unit for hackertours --- hackertours.unit | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 hackertours.unit diff --git a/hackertours.unit b/hackertours.unit new file mode 100644 index 0000000..b986db1 --- /dev/null +++ b/hackertours.unit @@ -0,0 +1,10 @@ +[Unit] +Description=Run foobazdmx controller in Hackertours config + +[Service] +Type=simple +ExecStart=poetry python ./foobaz.py -a 127.0.0.1 -r hackertours -A hackertours -C 0,255,32 +WorkingDirectory=~/working/foobazdmx + +[Install] +WantedBy=default.target From 77cea0bcc06681ae77682f3fc1a4b3e99b361013 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 17 Dec 2024 10:16:32 +0100 Subject: [PATCH 9/9] Correct syntax and name --- hackertours.service | 13 +++++++++++++ hackertours.unit | 10 ---------- 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 hackertours.service delete mode 100644 hackertours.unit diff --git a/hackertours.service b/hackertours.service new file mode 100644 index 0000000..8c4d774 --- /dev/null +++ b/hackertours.service @@ -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 diff --git a/hackertours.unit b/hackertours.unit deleted file mode 100644 index b986db1..0000000 --- a/hackertours.unit +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Run foobazdmx controller in Hackertours config - -[Service] -Type=simple -ExecStart=poetry python ./foobaz.py -a 127.0.0.1 -r hackertours -A hackertours -C 0,255,32 -WorkingDirectory=~/working/foobazdmx - -[Install] -WantedBy=default.target