update to support multiple universes per worker
This commit is contained in:
parent
b4e6851e32
commit
5d7f48d813
11 changed files with 156 additions and 189 deletions
54
conf.py
54
conf.py
|
@ -7,7 +7,7 @@ except ImportError:
|
||||||
import logging
|
import logging
|
||||||
from sys import exit
|
from sys import exit
|
||||||
|
|
||||||
LOG = logging.getLogger('Config')
|
LOG = logging.getLogger("Config")
|
||||||
|
|
||||||
|
|
||||||
class ConfigWrapper:
|
class ConfigWrapper:
|
||||||
|
@ -19,45 +19,43 @@ class ConfigWrapper:
|
||||||
|
|
||||||
def load_and_validate_config(path):
|
def load_and_validate_config(path):
|
||||||
try:
|
try:
|
||||||
with open(path, 'r') as cf:
|
with open(path, "r") as cf:
|
||||||
config = toml_load(cf.read())
|
config = toml_load(cf.read())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(f'{path} is no valid toml configuration file')
|
LOG.exception(f"{path} is no valid toml configuration file")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
if not config.get('mqtt', {}).get('host'):
|
if not config.get("mqtt", {}).get("host"):
|
||||||
LOG.error(
|
LOG.error(
|
||||||
f'configuration option "mqtt" "host" is missing in config, but required to exist'
|
f'configuration option "mqtt" "host" is missing in config, but required to exist'
|
||||||
)
|
)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
universes = {}
|
||||||
|
used_universes = {}
|
||||||
|
for universe, uconfig in config.get("universes", {}).items():
|
||||||
|
universes[universe] = ConfigWrapper(
|
||||||
|
alert_brightness=max(int(uconfig.get("alert_brightness", 255)), 10),
|
||||||
|
filters=uconfig.get("filters", []),
|
||||||
|
lights=uconfig.get("lights", {}),
|
||||||
|
multicast=bool(uconfig.get("multicast", False) is True),
|
||||||
|
rainbow_brightness=int(uconfig.get("rainbow_brightness", 150)),
|
||||||
|
target=uconfig.get("target", "127.0.0.1"),
|
||||||
|
universe=int(uconfig.get("universe", 1)),
|
||||||
|
)
|
||||||
|
if universes[universe].universe in used_universes:
|
||||||
|
LOG.warning(
|
||||||
|
f"universe {universes[universe].universe} used by both {universe} and {used_universes[universes[universe]].universe}"
|
||||||
|
)
|
||||||
|
|
||||||
conf = ConfigWrapper(
|
conf = ConfigWrapper(
|
||||||
mqtt=ConfigWrapper(
|
mqtt=ConfigWrapper(
|
||||||
host=config['mqtt']['host'],
|
host=config["mqtt"]["host"],
|
||||||
user=config['mqtt'].get('user'),
|
user=config["mqtt"].get("user"),
|
||||||
password=config['mqtt'].get('password'),
|
password=config["mqtt"].get("password"),
|
||||||
topic=config['mqtt'].get('topic', '/voc/alert'),
|
topic=config["mqtt"].get("topic", "/voc/alert"),
|
||||||
),
|
),
|
||||||
sacn=ConfigWrapper(
|
universes=universes,
|
||||||
multicast=bool(config.get('sacn', {}).get('multicast', False) is True),
|
|
||||||
target=config.get('sacn', {}).get('target', '127.0.0.1'),
|
|
||||||
universe=int(config.get('sacn', {}).get('universe', 1)),
|
|
||||||
),
|
|
||||||
alerts=ConfigWrapper(
|
|
||||||
brightness=max(int(config.get('alerts', {}).get('brightness', 255)), 10),
|
|
||||||
filters=sorted(config.get('alerts', {}).get('filters', set())),
|
|
||||||
),
|
|
||||||
rainbow=ConfigWrapper(
|
|
||||||
enable=bool(config.get('rainbow', {}).get('enable', True) is True),
|
|
||||||
intensity=max(int(config.get('rainbow', {}).get('intensity', 100)), 10),
|
|
||||||
brightness=max(int(config.get('rainbow', {}).get('brightness', 150)), 10),
|
|
||||||
speed=int(config.get('rainbow', {}).get('speed', 25)),
|
|
||||||
),
|
|
||||||
lights=config.get('lights', {}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if conf.alerts.brightness < conf.rainbow.brightness:
|
|
||||||
LOG.error('alerts brightness must be equal or above rainbow brightness')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return conf
|
return conf
|
||||||
|
|
|
@ -11,8 +11,8 @@ user = ""
|
||||||
password = ""
|
password = ""
|
||||||
topic = "/voc/alert"
|
topic = "/voc/alert"
|
||||||
|
|
||||||
|
# "demo" can be anything you like
|
||||||
[sacn]
|
[universes.demo]
|
||||||
# Wether to enable sACN multicast. Default is off.
|
# Wether to enable sACN multicast. Default is off.
|
||||||
multicast = false
|
multicast = false
|
||||||
|
|
||||||
|
@ -23,42 +23,24 @@ target = "127.0.0.1"
|
||||||
# which universe to address
|
# which universe to address
|
||||||
universe = 1
|
universe = 1
|
||||||
|
|
||||||
|
|
||||||
[alerts]
|
|
||||||
# This specifies the maximum DMX dimmer value that's sent to your lights
|
|
||||||
# when alerts occur. This must be atleast the same or more as the
|
|
||||||
# rainbow brightness (see below).
|
|
||||||
brightness = 255
|
|
||||||
|
|
||||||
# Filter by specific components. If this list is non-empty, the message
|
# Filter by specific components. If this list is non-empty, the message
|
||||||
# will get shown if atleast one of these filters match. The filters are
|
# will get shown if atleast one of these filters match. The filters are
|
||||||
# applied by using re.search() on the component part of the message.
|
# applied by using re.search() on the component part of the message.
|
||||||
filters = []
|
filters = []
|
||||||
|
|
||||||
|
# This specifies the maximum DMX dimmer value that's sent to your lights
|
||||||
[rainbow]
|
# when alerts occur. This must be atleast the same or more as the
|
||||||
# Wether to enable the rainbow 'no alerts' loop. If false, all other
|
# rainbow brightness (see below).
|
||||||
# options in here will be ignored.
|
alert_brightness = 255
|
||||||
enable = true
|
|
||||||
|
|
||||||
# This directly controls the 'value' part of the HSV equation in the
|
|
||||||
# rainbow 'no alerts' break loop. Value must be between 10% and 100%.
|
|
||||||
intensity = 100
|
|
||||||
|
|
||||||
# DMX dimmer value when displaying the rainbow pattern. Must be equal
|
# DMX dimmer value when displaying the rainbow pattern. Must be equal
|
||||||
# or below the generic 'brightness' value above.
|
# or below the generic 'brightness' value above. Set to 0 to disable
|
||||||
brightness = 150
|
# the rainbow
|
||||||
|
rainbow_brightness = 150
|
||||||
# Speed of the rainbow pattern. This is specified as "miliseconds
|
|
||||||
# between rotating the hue wheel by 1 degree". Minimum value is 25,
|
|
||||||
# because sACN does not support more than 40 fps. Setting it any lower
|
|
||||||
# will disable the animation altogehter, resulting in static lights.
|
|
||||||
speed = 25
|
|
||||||
|
|
||||||
|
|
||||||
# This contains the DMX start addresses of your light fixtures. You
|
# This contains the DMX start addresses of your light fixtures. You
|
||||||
# have to add atleast one fixture for the software to work.
|
# have to add atleast one fixture for the software to work.
|
||||||
[lights]
|
[universes.demo.lights]
|
||||||
ignition_wal_l710 = []
|
ignition_wal_l710 = []
|
||||||
stairville_par_56 = []
|
stairville_par_56 = []
|
||||||
tsss_led_par_rgbw = []
|
tsss_led_par_rgbw = []
|
||||||
|
|
99
dmx_queue.py
99
dmx_queue.py
|
@ -5,14 +5,20 @@ from time import sleep
|
||||||
|
|
||||||
from sacn import sACNsender
|
from sacn import sACNsender
|
||||||
|
|
||||||
LOG = logging.getLogger('DMXQueue')
|
import lights
|
||||||
|
|
||||||
|
|
||||||
class DMXQueue:
|
class DMXQueue:
|
||||||
def __init__(self, config, queue, lights):
|
def __init__(self, config, universe, queue):
|
||||||
self.config = config
|
self.log = logging.getLogger(f"DMXQueue {universe}")
|
||||||
|
self.config = config.universes[universe]
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.lights = lights
|
|
||||||
|
self.lights = []
|
||||||
|
for classname, addrs in self.config.lights.items():
|
||||||
|
cls = getattr(lights, classname)
|
||||||
|
for addr in addrs:
|
||||||
|
self.lights.append(cls(addr))
|
||||||
|
|
||||||
self.worker = Thread(target=self._worker)
|
self.worker = Thread(target=self._worker)
|
||||||
self.worker_should_be_running = False
|
self.worker_should_be_running = False
|
||||||
|
@ -23,11 +29,11 @@ class DMXQueue:
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.sacn.start()
|
self.sacn.start()
|
||||||
self.sacn.activate_output(self.config.sacn.universe)
|
self.sacn.activate_output(self.config.universe)
|
||||||
|
|
||||||
self.sacn[self.config.sacn.universe].multicast = self.config.sacn.multicast
|
self.sacn[self.config.universe].multicast = self.config.multicast
|
||||||
if not self.config.sacn.multicast:
|
if not self.config.multicast:
|
||||||
self.sacn[self.config.sacn.universe].destination = self.config.sacn.target
|
self.sacn[self.config.universe].destination = self.config.target
|
||||||
|
|
||||||
self.dmx_data = 512 * [0]
|
self.dmx_data = 512 * [0]
|
||||||
|
|
||||||
|
@ -35,14 +41,14 @@ class DMXQueue:
|
||||||
self.worker.start()
|
self.worker.start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
LOG.info('Waiting for worker to terminate ...')
|
self.log.info("Waiting for worker to terminate ...")
|
||||||
self.worker_should_be_running = False
|
self.worker_should_be_running = False
|
||||||
self.worker.join()
|
self.worker.join()
|
||||||
self.sacn.stop()
|
self.sacn.stop()
|
||||||
|
|
||||||
def _dmx(self, addr, data):
|
def _dmx(self, addr, data):
|
||||||
self.dmx_data[addr - 1] = data
|
self.dmx_data[addr - 1] = data
|
||||||
self.sacn[self.config.sacn.universe].dmx_data = tuple(self.dmx_data)
|
self.sacn[self.config.universe].dmx_data = tuple(self.dmx_data)
|
||||||
|
|
||||||
def _bulk(self, start_addr, values):
|
def _bulk(self, start_addr, values):
|
||||||
for idx, value in enumerate(values):
|
for idx, value in enumerate(values):
|
||||||
|
@ -58,29 +64,39 @@ class DMXQueue:
|
||||||
self._bulk(*light.dump())
|
self._bulk(*light.dump())
|
||||||
|
|
||||||
def _worker(self):
|
def _worker(self):
|
||||||
LOG.info('Worker startup')
|
self.log.info("Worker startup")
|
||||||
rotation = 0
|
rotation = 0
|
||||||
while self.worker_should_be_running:
|
while self.worker_should_be_running:
|
||||||
try:
|
try:
|
||||||
level, component, text = self.queue.get_nowait()
|
level, component, text = self.queue.get_nowait()
|
||||||
|
|
||||||
LOG.info(f'Got queue item: {level} {component} : {text}')
|
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}")
|
||||||
|
|
||||||
# effect duration should be between 1s and 1.5s
|
# effect duration should be between 1s and 1.5s
|
||||||
if level == 'error':
|
if level == "error":
|
||||||
self._update_all(0, 0, 0, 0, 0)
|
self._update_all(0, 0, 0, 0, 0)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
# three instances of two flashes each
|
# three instances of two flashes each
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
self._update_all(
|
self._update_all(
|
||||||
self.config.alerts.brightness, 255, 0, 0, 50
|
self.config.alert_brightness, 255, 0, 0, 50
|
||||||
)
|
)
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self._update_all(0, 255, 0, 0, 50)
|
self._update_all(0, 255, 0, 0, 50)
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
elif level == 'warn':
|
elif level == "warn":
|
||||||
self._update_all(0, 0, 0, 0, 0)
|
self._update_all(0, 0, 0, 0, 0)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
# warning: blink alternate, but slow
|
# warning: blink alternate, but slow
|
||||||
|
@ -92,7 +108,7 @@ class DMXQueue:
|
||||||
light.white = 50
|
light.white = 50
|
||||||
|
|
||||||
if (idx + i) % 2:
|
if (idx + i) % 2:
|
||||||
light.intensity = self.config.alerts.brightness
|
light.intensity = self.config.alert_brightness
|
||||||
else:
|
else:
|
||||||
light.intensity = 0
|
light.intensity = 0
|
||||||
|
|
||||||
|
@ -100,25 +116,25 @@ class DMXQueue:
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
self._update_all(0, 0, 0, 0, 0)
|
self._update_all(0, 0, 0, 0, 0)
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
elif level == 'info':
|
elif level == "info":
|
||||||
forward = list(range(15))
|
forward = list(range(15))
|
||||||
reverse = list(range(15))
|
reverse = list(range(15))
|
||||||
reverse.reverse()
|
reverse.reverse()
|
||||||
|
|
||||||
if self.config.rainbow.enable:
|
if self.config.rainbow_brightness > 0:
|
||||||
diff = (
|
diff = (
|
||||||
self.config.alerts.brightness
|
self.config.alert_brightness
|
||||||
- self.config.rainbow.brightness
|
- self.config.rainbow_brightness
|
||||||
)
|
)
|
||||||
LOG.debug(diff)
|
self.log.debug(diff)
|
||||||
|
|
||||||
if diff >= 50:
|
if diff >= 50:
|
||||||
for idx in forward + reverse:
|
for idx in forward + reverse:
|
||||||
LOG.debug(idx)
|
self.log.debug(idx)
|
||||||
LOG.debug(diff * idx)
|
self.log.debug(diff * idx)
|
||||||
self._update_all(
|
self._update_all(
|
||||||
int(
|
int(
|
||||||
self.config.rainbow.brightness
|
self.config.rainbow_brightness
|
||||||
+ ((diff / len(forward)) * idx)
|
+ ((diff / len(forward)) * idx)
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
|
@ -129,10 +145,10 @@ class DMXQueue:
|
||||||
sleep(0.025)
|
sleep(0.025)
|
||||||
else:
|
else:
|
||||||
for idx in forward + reverse:
|
for idx in forward + reverse:
|
||||||
LOG.debug(idx)
|
self.log.debug(idx)
|
||||||
self._update_all(
|
self._update_all(
|
||||||
int(
|
int(
|
||||||
(self.config.alerts.brightness / len(forward)) * idx
|
(self.config.alert_brightness / len(forward)) * idx
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
50,
|
50,
|
||||||
|
@ -143,24 +159,23 @@ class DMXQueue:
|
||||||
self._update_all(0, 0, 0, 0, 0)
|
self._update_all(0, 0, 0, 0, 0)
|
||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
except Empty:
|
except Empty:
|
||||||
if self.config.rainbow.enable:
|
if self.config.rainbow_brightness > 0:
|
||||||
for idx, light in enumerate(self.lights):
|
for idx, light in enumerate(self.lights):
|
||||||
self._bulk(*light.rainbow(
|
self._bulk(
|
||||||
idx,
|
*light.rainbow(
|
||||||
rotation,
|
idx,
|
||||||
len(self.lights),
|
rotation,
|
||||||
self.config.rainbow.intensity,
|
len(self.lights),
|
||||||
self.config.rainbow.brightness,
|
100,
|
||||||
))
|
self.config.rainbow_brightness,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if self.config.rainbow.speed >= 25:
|
rotation = rotation + 1
|
||||||
rotation = rotation + 1
|
if rotation >= 360:
|
||||||
if rotation >= 360:
|
rotation = 0
|
||||||
rotation = 0
|
|
||||||
|
|
||||||
sleep(self.config.rainbow.speed / 1000)
|
sleep(0.03)
|
||||||
else:
|
|
||||||
sleep(0.2)
|
|
||||||
else:
|
else:
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
LOG.info('Worker shutdown')
|
self.log.info("Worker shutdown")
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from .ignition_wal_l710 import IgnitionWALL710
|
||||||
|
from .pulsar_chromaflood_200 import PulsarChromaflood200
|
||||||
|
from .sheds_30w_cob_rgb import Sheds30WCOBRGB
|
||||||
|
from .stairville_par_56 import StairvillePar56
|
||||||
|
from .tsss_led_par_rgbw import TSSS_LED_PAR_RGBW
|
||||||
|
from .varytec_hero_wash_zoom_712 import VarytecHeroWashZoom712
|
||||||
|
from .wled import WLED
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from colorsys import hsv_to_rgb
|
from colorsys import hsv_to_rgb
|
||||||
|
|
||||||
LOG = logging.getLogger('DMX')
|
LOG = logging.getLogger("DMX")
|
||||||
|
|
||||||
|
|
||||||
class BaseDMXLight:
|
class BaseDMXLight:
|
||||||
|
@ -14,20 +14,18 @@ class BaseDMXLight:
|
||||||
self.white = 0
|
self.white = 0
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.name} ({self.address})'
|
return f"{self.name} ({self.address})"
|
||||||
|
|
||||||
def _dump(self):
|
def _dump(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
ret = self._dump()
|
ret = self._dump()
|
||||||
LOG.debug(f'{str(self)} -> {ret[1]}')
|
LOG.debug(f"{str(self)} -> {ret[1]}")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def rainbow(self, idx, angle, number_of_lights, intensity, brightness):
|
def rainbow(self, idx, angle, number_of_lights, intensity, brightness):
|
||||||
my_degrees_dec = (
|
my_degrees_dec = (angle + (idx * (360 / number_of_lights))) % 360 / 360
|
||||||
(angle + (idx * (360 / number_of_lights))) % 360 / 360
|
|
||||||
)
|
|
||||||
r, g, b = hsv_to_rgb(
|
r, g, b = hsv_to_rgb(
|
||||||
my_degrees_dec,
|
my_degrees_dec,
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from colorsys import hsv_to_rgb
|
from colorsys import hsv_to_rgb
|
||||||
|
|
||||||
from .common import BaseDMXLight
|
from .common import BaseDMXLight
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +11,11 @@ class PulsarChromaflood200(BaseDMXLight):
|
||||||
self.red,
|
self.red,
|
||||||
self.green,
|
self.green,
|
||||||
self.blue,
|
self.blue,
|
||||||
0,0,0, # chase 1
|
0,
|
||||||
0,0,0, # chase 2
|
0,
|
||||||
self.intensity
|
0, # chase 1
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, # chase 2
|
||||||
|
self.intensity,
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Sheds30WCOBRGB(BaseDMXLight):
|
||||||
self.red,
|
self.red,
|
||||||
self.green,
|
self.green,
|
||||||
self.blue,
|
self.blue,
|
||||||
0, # strobe
|
0, # strobe
|
||||||
0, # mode
|
0, # mode
|
||||||
0, # speed
|
0, # speed
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,9 +7,9 @@ class StairvillePar56(BaseDMXLight):
|
||||||
def _dump(self):
|
def _dump(self):
|
||||||
offset = self.intensity / 255
|
offset = self.intensity / 255
|
||||||
return self.address, [
|
return self.address, [
|
||||||
0, # RGB mode
|
0, # RGB mode
|
||||||
int(self.red * offset),
|
int(self.red * offset),
|
||||||
int(self.green * offset),
|
int(self.green * offset),
|
||||||
int(self.blue * offset),
|
int(self.blue * offset),
|
||||||
0, # speed
|
0, # speed
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,9 +6,9 @@ class TSSS_LED_PAR_RGBW(BaseDMXLight):
|
||||||
|
|
||||||
def _dump(self):
|
def _dump(self):
|
||||||
return self.address, [
|
return self.address, [
|
||||||
0, # function
|
0, # function
|
||||||
0, # mode
|
0, # mode
|
||||||
0, # speed
|
0, # speed
|
||||||
self.intensity,
|
self.intensity,
|
||||||
self.red,
|
self.red,
|
||||||
self.green,
|
self.green,
|
||||||
|
|
71
main.py
71
main.py
|
@ -8,82 +8,53 @@ from time import sleep
|
||||||
|
|
||||||
from conf import load_and_validate_config
|
from conf import load_and_validate_config
|
||||||
from dmx_queue import DMXQueue
|
from dmx_queue import DMXQueue
|
||||||
from lights.ignition_wal_l710 import IgnitionWALL710
|
|
||||||
from lights.pulsar_chromaflood_200 import PulsarChromaflood200
|
|
||||||
from lights.sheds_30w_cob_rgb import Sheds30WCOBRGB
|
|
||||||
from lights.stairville_par_56 import StairvillePar56
|
|
||||||
from lights.varytec_hero_wash_zoom_712 import VarytecHeroWashZoom712
|
|
||||||
from lights.tsss_led_par_rgbw import TSSS_LED_PAR_RGBW
|
|
||||||
from lights.wled import WLED
|
|
||||||
from mqtt_queue import MQTTQueue
|
from mqtt_queue import MQTTQueue
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format='%(asctime)s %(name)20s [%(levelname)-8s] %(message)s',
|
format="%(asctime)s %(name)20s [%(levelname)-8s] %(message)s",
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG = logging.getLogger('main')
|
LOG = logging.getLogger("main")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--config',
|
"--config",
|
||||||
default='config.toml',
|
default="config.toml",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
config = load_and_validate_config(args.config)
|
config = load_and_validate_config(args.config)
|
||||||
|
|
||||||
LOG.info('Welcome to Voc2DMX')
|
LOG.info("Welcome to Voc2DMX")
|
||||||
|
|
||||||
queue = Queue()
|
queues = {}
|
||||||
|
dmx_workers = {}
|
||||||
|
|
||||||
lights = []
|
LOG.info("Initializing worker queues ...")
|
||||||
for addr in config.lights.get('ignition_wal_l710', []):
|
|
||||||
lights.append(IgnitionWALL710(addr))
|
|
||||||
for addr in config.lights.get('pulsar_chromaflood_200', []):
|
|
||||||
lights.append(PulsarChromaflood200(addr))
|
|
||||||
for addr in config.lights.get('sheds_30w_cob_rgb', []):
|
|
||||||
lights.append(Sheds30WCOBRGB(addr))
|
|
||||||
for addr in config.lights.get('stairville_par_56', []):
|
|
||||||
lights.append(StairvillePar56(addr))
|
|
||||||
for addr in config.lights.get('tsss_led_par_rgbw', []):
|
|
||||||
lights.append(TSSS_LED_PAR_RGBW(addr))
|
|
||||||
for addr in config.lights.get('varytec_hero_wash_712_zoom', []):
|
|
||||||
lights.append(VarytecHeroWashZoom712(addr))
|
|
||||||
for addr in config.lights.get('wled_multi_rgb', []):
|
|
||||||
lights.append(WLED(addr))
|
|
||||||
|
|
||||||
if not lights:
|
|
||||||
LOG.error('No lights configured, please add atleast one fixture')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
LOG.info('')
|
|
||||||
LOG.info('Configured lights:')
|
|
||||||
for light in lights:
|
|
||||||
LOG.info(light)
|
|
||||||
LOG.info('')
|
|
||||||
|
|
||||||
LOG.info('Initializing worker queues ...')
|
|
||||||
|
|
||||||
mqttq = MQTTQueue(config, queue)
|
|
||||||
dmxq = DMXQueue(config, queue, lights)
|
|
||||||
|
|
||||||
|
mqttq = MQTTQueue(config, queues)
|
||||||
mqttq.start()
|
mqttq.start()
|
||||||
dmxq.start()
|
|
||||||
|
|
||||||
LOG.info('initialization done, now running. Press Ctrl-C to stop')
|
for universe in config.universes:
|
||||||
|
queues[universe] = Queue()
|
||||||
|
dmx_workers[universe] = DMXQueue(config, universe, queues[universe])
|
||||||
|
dmx_workers[universe].start()
|
||||||
|
|
||||||
|
LOG.info("initialization done, now running. Press Ctrl-C to stop")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
sleep(1)
|
sleep(1)
|
||||||
except KeyboardInterrupt:
|
finally:
|
||||||
LOG.warning('Got interrupt, stopping queues ...')
|
LOG.warning("Got interrupt, stopping queues ...")
|
||||||
mqttq.stop()
|
mqttq.stop()
|
||||||
dmxq.stop()
|
for universe in config.universes:
|
||||||
|
dmx_workers[universe].stop()
|
||||||
|
|
||||||
LOG.info('Bye!')
|
LOG.info("Bye!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -5,14 +5,14 @@ from queue import Queue
|
||||||
|
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
LOG = logging.getLogger('MQTTQueue')
|
LOG = logging.getLogger("MQTTQueue")
|
||||||
|
|
||||||
|
|
||||||
class MQTTQueue:
|
class MQTTQueue:
|
||||||
def __init__(self, config, queue):
|
def __init__(self, config, queues):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.client = mqtt.Client()
|
self.client = mqtt.Client()
|
||||||
self.queue = queue
|
self.queues = queues
|
||||||
|
|
||||||
self.client.on_connect = self.on_connect
|
self.client.on_connect = self.on_connect
|
||||||
self.client.on_disconnect = self.on_disconnect
|
self.client.on_disconnect = self.on_disconnect
|
||||||
|
@ -32,38 +32,29 @@ class MQTTQueue:
|
||||||
self.client.disconnect()
|
self.client.disconnect()
|
||||||
|
|
||||||
def on_connect(self, client, userdata, flags, rc):
|
def on_connect(self, client, userdata, flags, rc):
|
||||||
LOG.info(f'Connected to {self.config.mqtt.host} with code {rc}')
|
LOG.info(f"Connected to {self.config.mqtt.host} with code {rc}")
|
||||||
|
|
||||||
self.client.subscribe(self.config.mqtt.topic)
|
self.client.subscribe(self.config.mqtt.topic)
|
||||||
LOG.info(f'Subscribed')
|
LOG.info(f"Subscribed")
|
||||||
|
|
||||||
def on_disconnect(self, client, userdata, rc):
|
def on_disconnect(self, client, userdata, rc):
|
||||||
LOG.warning(f'Disconnected from {self.config.mqtt.host} with code {rc}')
|
LOG.warning(f"Disconnected from {self.config.mqtt.host} with code {rc}")
|
||||||
|
|
||||||
def on_mqtt_message(self, client, userdata, msg):
|
def on_mqtt_message(self, client, userdata, msg):
|
||||||
try:
|
try:
|
||||||
data = json.loads(msg.payload.decode('utf-8'))
|
data = json.loads(msg.payload.decode("utf-8"))
|
||||||
|
text = re.sub(r"\<[a-z\/]+\>", "", data["msg"])
|
||||||
|
|
||||||
text = re.sub(r'\<[a-z\/]+\>', '', data['msg'])
|
for queue in self.queues:
|
||||||
|
self.queues[queue].put(
|
||||||
add_to_queue = True
|
|
||||||
if self.config.alerts.filters:
|
|
||||||
add_to_queue = False
|
|
||||||
for f in self.config.alerts.filters:
|
|
||||||
if re.search(f, data['component'], re.IGNORECASE):
|
|
||||||
add_to_queue = True
|
|
||||||
break # no point in searching further
|
|
||||||
|
|
||||||
if add_to_queue:
|
|
||||||
self.queue.put(
|
|
||||||
(
|
(
|
||||||
data['level'].lower(),
|
data["level"].lower(),
|
||||||
data['component'],
|
data["component"],
|
||||||
text,
|
text,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
LOG.info(f'Put queue item: {data["level"].lower()} {data["component"]} : {text}')
|
LOG.info(
|
||||||
else:
|
f'Put queue {queue} item: {data["level"].lower()} {data["component"]} : {text}'
|
||||||
LOG.info(f'Ignoring message for {data["component"]} because it was filtered')
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(msg.payload)
|
LOG.exception(msg.payload)
|
||||||
|
|
Loading…
Reference in a new issue