viri-leds-dmx-sacn/dmx_queue.py

182 lines
6.5 KiB
Python
Raw Normal View History

2023-08-07 11:39:15 +00:00
import logging
from queue import Empty
from threading import Thread
from time import sleep
from sacn import sACNsender
import lights
2023-08-07 11:39:15 +00:00
2023-08-07 12:48:18 +00:00
2023-08-07 11:39:15 +00:00
class DMXQueue:
def __init__(self, config, universe, queue):
self.log = logging.getLogger(f"DMXQueue {universe}")
self.config = config.universes[universe]
2023-08-07 11:39:15 +00:00
self.queue = queue
self.lights = []
for classname, addrs in self.config.lights.items():
cls = getattr(lights, classname)
for addr in addrs:
self.lights.append(cls(addr))
2023-08-07 11:39:15 +00:00
self.worker = Thread(target=self._worker)
self.worker_should_be_running = False
self.sacn = sACNsender(
fps=40,
)
2023-08-07 11:39:15 +00:00
def start(self):
self.sacn.start()
self.sacn.activate_output(self.config.universe)
2023-08-07 11:39:15 +00:00
self.sacn[self.config.universe].multicast = self.config.multicast
if not self.config.multicast:
self.sacn[self.config.universe].destination = self.config.target
2023-08-07 11:39:15 +00:00
2023-08-07 12:48:18 +00:00
self.dmx_data = 512 * [0]
2023-08-07 11:39:15 +00:00
self.worker_should_be_running = True
self.worker.start()
def stop(self):
self.log.info("Waiting for worker to terminate ...")
2023-08-07 11:39:15 +00:00
self.worker_should_be_running = False
self.worker.join()
self.sacn.stop()
def _dmx(self, addr, data):
2023-08-07 12:48:18 +00:00
self.dmx_data[addr - 1] = data
self.sacn[self.config.universe].dmx_data = tuple(self.dmx_data)
2023-08-07 11:39:15 +00:00
def _bulk(self, start_addr, values):
for idx, value in enumerate(values):
2023-08-07 12:48:18 +00:00
self._dmx(start_addr + idx, value)
2023-08-07 11:39:15 +00:00
2023-08-16 20:47:12 +00:00
def _update_all(self, intensity, red, green, blue, white):
2023-08-07 11:39:15 +00:00
for light in self.lights:
light.intensity = intensity
light.red = red
light.green = green
light.blue = blue
light.white = white
self._bulk(*light.dump())
def _worker(self):
self.log.info("Worker startup")
2023-08-07 12:45:01 +00:00
rotation = 0
2023-08-07 11:39:15 +00:00
while self.worker_should_be_running:
try:
level, component, text = self.queue.get_nowait()
if self.config.filters:
filtered = True
for f in self.config.filters:
if re.search(f, component, re.IGNORECASE):
filtered = False
break # no point in searching further
if filtered:
# no alert for filtered messages
continue
self.log.info(f"Got queue item: {level} {component} : {text}")
2023-08-08 04:40:54 +00:00
# effect duration should be between 1s and 1.5s
if level == "error":
2023-08-16 20:47:12 +00:00
self._update_all(0, 0, 0, 0, 0)
2023-08-13 21:28:14 +00:00
sleep(0.2)
2023-08-07 11:39:15 +00:00
# three instances of two flashes each
for i in range(3):
2023-08-08 04:40:54 +00:00
for j in range(2):
2023-08-16 20:47:12 +00:00
self._update_all(
self.config.alert_brightness, 255, 0, 0, 50
2023-08-16 20:47:12 +00:00
)
2023-08-07 11:39:15 +00:00
sleep(0.1)
2023-08-16 20:47:12 +00:00
self._update_all(0, 255, 0, 0, 50)
2023-08-07 11:39:15 +00:00
sleep(0.1)
sleep(0.2)
elif level == "warn":
2023-08-16 20:47:12 +00:00
self._update_all(0, 0, 0, 0, 0)
2023-08-13 21:28:14 +00:00
sleep(0.2)
2023-08-07 11:39:15 +00:00
# warning: blink alternate, but slow
for i in range(6):
for idx, light in enumerate(self.lights):
light.red = 255
light.green = 150
light.blue = 0
2023-08-16 20:47:12 +00:00
light.white = 50
2023-08-07 11:39:15 +00:00
if (idx + i) % 2:
light.intensity = self.config.alert_brightness
2023-08-07 11:39:15 +00:00
else:
light.intensity = 0
self._bulk(*light.dump())
2023-08-08 04:40:54 +00:00
sleep(0.2)
2023-08-16 20:47:12 +00:00
self._update_all(0, 0, 0, 0, 0)
sleep(0.2)
elif level == "info":
2023-08-08 04:40:54 +00:00
forward = list(range(15))
reverse = list(range(15))
reverse.reverse()
if self.config.rainbow_brightness > 0:
2023-08-13 21:28:14 +00:00
diff = (
self.config.alert_brightness
- self.config.rainbow_brightness
2023-08-13 21:28:14 +00:00
)
self.log.debug(diff)
2023-08-13 21:28:14 +00:00
if diff >= 50:
for idx in forward + reverse:
self.log.debug(idx)
self.log.debug(diff * idx)
2023-08-13 21:28:14 +00:00
self._update_all(
int(
self.config.rainbow_brightness
2023-08-16 20:41:34 +00:00
+ ((diff / len(forward)) * idx)
2023-08-13 21:28:14 +00:00
),
0,
50,
255,
2023-08-16 20:47:12 +00:00
50,
2023-08-13 21:28:14 +00:00
)
sleep(0.025)
else:
for idx in forward + reverse:
self.log.debug(idx)
self._update_all(
int(
(self.config.alert_brightness / len(forward)) * idx
),
0,
50,
255,
50,
)
sleep(0.025)
self._update_all(0, 0, 0, 0, 0)
2023-08-07 11:39:15 +00:00
self.queue.task_done()
except Empty:
if self.config.rainbow_brightness > 0:
2023-08-07 18:05:20 +00:00
for idx, light in enumerate(self.lights):
self._bulk(
*light.rainbow(
idx,
rotation,
len(self.lights),
100,
self.config.rainbow_brightness,
)
)
rotation = rotation + 1
if rotation >= 360:
rotation = 0
sleep(0.03)
2023-08-07 18:05:20 +00:00
else:
sleep(0.2)
self.log.info("Worker shutdown")