201 lines
6.3 KiB
Python
201 lines
6.3 KiB
Python
|
#!/usr/bin/python3
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import json
|
||
|
import subprocess
|
||
|
from time import sleep
|
||
|
from PyDMXControl.controllers import OpenDMXController
|
||
|
from PyDMXControl.profiles.Generic import Dimmer
|
||
|
import traceback
|
||
|
|
||
|
'''
|
||
|
# "channel" --> channel/line identifier, order as connected to DMX decoder
|
||
|
# seq -> "sequence" = position (seconds) on 'timeline' starting 0
|
||
|
# fadein / fadeout -> milliseconds
|
||
|
# duration -> seconds
|
||
|
# brightness -> 0-255 (optional entry in json, else use default)
|
||
|
'''
|
||
|
|
||
|
base = os.path.expanduser("~/bin/dmx")
|
||
|
## COMMENT THIS SECTION WHEN TIMELINE IS FINAL
|
||
|
with open(f"{base}/getseq/getspread.py") as getspread:
|
||
|
exec(getspread.read())
|
||
|
##
|
||
|
|
||
|
# DEFAULTS
|
||
|
# 0-255 dim / brightness range
|
||
|
default_brightness = 5
|
||
|
# fadein, fadeout (milliseconds)
|
||
|
default_fadein = 500
|
||
|
default_fadeout = 300
|
||
|
## if "start' not entered in 'start_opt' column, default start is 0
|
||
|
## subtract start_opt_time from on and off events
|
||
|
start_opt_time = 0
|
||
|
|
||
|
|
||
|
data = json.load(open('sequence.json'))
|
||
|
|
||
|
## ON events
|
||
|
def start_event(channel):
|
||
|
channel_start = {}
|
||
|
channel_start["channel"] = channel["channel"]
|
||
|
channel_start["event"] = "on"
|
||
|
channel_start["seq"] = channel["seq"] - start_opt_time
|
||
|
if "brightness" in channel:
|
||
|
channel_start["brightness"] = int(channel["brightness"])
|
||
|
if "fadein" in channel:
|
||
|
channel_start["fadein"] = int(channel["fadein"])
|
||
|
return channel_start
|
||
|
|
||
|
## OFF events
|
||
|
def stop_event(channel):
|
||
|
channel_stop = {}
|
||
|
channel_stop["channel"] = channel["channel"]
|
||
|
channel_stop["event"] = "off"
|
||
|
# duration from 0
|
||
|
stop_time = channel["seq"] + channel["duration"] - start_opt_time
|
||
|
channel_stop["seq"] = stop_time
|
||
|
if "fadeout" in channel:
|
||
|
channel_stop["fadeout"] = int(channel["fadeout"])
|
||
|
return channel_stop
|
||
|
|
||
|
## each 'time' (sec) an event occurs
|
||
|
def add_event(events_dict, event):
|
||
|
if event["seq"] not in events:
|
||
|
events_dict[event["seq"]] = []
|
||
|
# add start/on and stop/off events to respective times
|
||
|
events_dict[event["seq"]].append(event)
|
||
|
return
|
||
|
|
||
|
data_connected = [ l for l in data \
|
||
|
if ("seq" in l.keys() and l["seq"]!="") \
|
||
|
and ("duration" in l.keys() and l["duration"] != "") ]
|
||
|
## sort by sequence -> play in this order (timeline)
|
||
|
sequence = sorted(data_connected, key=lambda x: x["seq"])
|
||
|
# print(sequence)
|
||
|
|
||
|
# start_opt_time -> if 'start' in calc, start playing from that point
|
||
|
for i in sequence:
|
||
|
if "start_opt" in i:
|
||
|
start_opt_time = int(i['seq'])
|
||
|
break
|
||
|
|
||
|
## generate all (on / off) events
|
||
|
events = {}
|
||
|
for i in sequence:
|
||
|
i_start = start_event(i)
|
||
|
i_stop = stop_event(i)
|
||
|
# add start/on and stop/off events to respective 'times'
|
||
|
add_event(events, i_start)
|
||
|
add_event(events, i_stop)
|
||
|
|
||
|
# chronologically sort event KEYS (time in sec / seq )
|
||
|
chrono = list(events.keys())
|
||
|
chrono.sort()
|
||
|
|
||
|
# chronological timeline with events (using sorted KEYS/time)
|
||
|
timeline = { x: events[x] for x in chrono if x >= 0 }
|
||
|
# if seq==0 and "event"=="off" -> remove / don't add [both on/off at 0]
|
||
|
for e in timeline[0]:
|
||
|
if e["event"] == "off":
|
||
|
timeline[0].remove(e)
|
||
|
|
||
|
# MID-START fix for 'previous-ONs' - on before start
|
||
|
channs = []
|
||
|
for t in timeline:
|
||
|
for c in timeline[t]:
|
||
|
if c['event'] == 'on':
|
||
|
channs.append(c['channel'])
|
||
|
if c['event'] == 'off':
|
||
|
if c['channel'] not in channs:
|
||
|
channs.append(c['channel'])
|
||
|
else:
|
||
|
channs.remove(c['channel'])
|
||
|
|
||
|
##print(channs)
|
||
|
for ch in channs:
|
||
|
timeline[0].append({"channel": ch, "event": "on", "seq": 0})
|
||
|
|
||
|
with open('timeline.json','w') as f:
|
||
|
json.dump(timeline, f, indent=2)
|
||
|
|
||
|
# wait for things to settle...
|
||
|
print("\n * Wait 3 seconds...")
|
||
|
sleep(3)
|
||
|
|
||
|
print("\n * Reset USB device")
|
||
|
subprocess.call(['usbreset', "FT232R USB UART"])
|
||
|
|
||
|
print("\n ** DMX start...")
|
||
|
dmx = OpenDMXController()
|
||
|
|
||
|
## sort by "channel" -> channel/line number
|
||
|
lines = sorted(data, key=lambda x: x["channel"])
|
||
|
## remove dupes and add lights to DMX once in this order --> [ channels ]
|
||
|
channels = []
|
||
|
for l in lines:
|
||
|
if l['channel'] not in channels:
|
||
|
channels.append(l['channel'])
|
||
|
line = dmx.add_fixture(Dimmer, name=l['channel'])
|
||
|
#print(channels)
|
||
|
#dmx.web_control()
|
||
|
|
||
|
try:
|
||
|
# loop 'forever'
|
||
|
while 1 > 0:
|
||
|
print()
|
||
|
# begin timeline
|
||
|
print(" * Start timeline...")
|
||
|
print(f" - start at seq: {start_opt_time}")
|
||
|
t = 0
|
||
|
# for every t (seq)
|
||
|
for i in timeline:
|
||
|
# print(i, timeline[i])
|
||
|
if int(i) > 0:
|
||
|
# hold, count time from last event
|
||
|
sleep(int(i) - t)
|
||
|
for e in timeline[i]:
|
||
|
# for all events at t=i (seq)
|
||
|
line = dmx.get_fixtures_by_name(e["channel"])[0]
|
||
|
if e["event"] == "on":
|
||
|
# dim value from json if there, else default
|
||
|
bright = e.get("brightness", default_brightness)
|
||
|
# fadein value from json if there, else default
|
||
|
fadein = e.get("fadein", default_fadein)
|
||
|
print(f" - {e['seq']}s | #{e['channel']} {e['event'].upper()} (brightness: {bright}, fadein: {fadein})")
|
||
|
line.dim(bright, fadein)
|
||
|
if e["event"] == "off":
|
||
|
# fadeout value from json if there, else default
|
||
|
fadeout = e.get("fadeout", default_fadeout)
|
||
|
print(f" - {e['seq']}s | #{e['channel']} {e['event'].upper()} (fadeout: {fadeout})")
|
||
|
line.dim(0, fadeout)
|
||
|
# now this is previous t for next event
|
||
|
t = i
|
||
|
# print to stdout next event info ## use chrono (keys list)
|
||
|
if i != chrono[-1]:
|
||
|
next_t = chrono[chrono.index(i)+1]
|
||
|
tt = next_t-int(i)
|
||
|
next_event = f" \ Next event [seq={next_t}] in {tt}s"
|
||
|
else:
|
||
|
next_event = " - Last event of sequence"
|
||
|
print(next_event)
|
||
|
except KeyboardInterrupt:
|
||
|
print()
|
||
|
for e in timeline[i]:
|
||
|
# for all events at t=i (seq)
|
||
|
line = dmx.get_fixtures_by_name(e["channel"])[0]
|
||
|
line.dim(0, 1000)
|
||
|
sleep(2)
|
||
|
dmx.close()
|
||
|
sys.exit()
|
||
|
except Exception as argh:
|
||
|
with open("zlog.txt", "a") as e:
|
||
|
e.write(" _____")
|
||
|
e.write(str(argh))
|
||
|
e.write(traceback.format_exc())
|
||
|
e.write('\n\n')
|
||
|
|
||
|
dmx.close()
|
||
|
|