dmx.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/opt/local/bin/python2.7
  2. # Append the path to help find our OLA packages
  3. import sys
  4. sys.path.append('/opt/local/lib/python2.7/site-packages')
  5. sys.path.append('/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages')
  6. import os
  7. import array
  8. import select
  9. import socket
  10. import string
  11. import subprocess
  12. from ola.ClientWrapper import ClientWrapper
  13. # ====================================
  14. # Globals
  15. # ====================================
  16. max_channels = 512
  17. wrapper = None
  18. state = [ 0 ] * max_channels
  19. cmds = { 'value' : [ 0 ] * max_channels, 'ticks' : [ 0 ] * max_channels, 'delay' : [ 0 ] * max_channels }
  20. sock = None
  21. max_mesg_len = 1024
  22. # ====================================
  23. # Defaults
  24. # ====================================
  25. interval = 50
  26. universe = 0
  27. # ====================================
  28. # Wrapper callback -- exit on errors
  29. # ====================================
  30. def DmxSent(state):
  31. # Stop on errors
  32. if not state.Succeeded():
  33. wrapper.Stop()
  34. # ====================================
  35. # Main calculation
  36. # ====================================
  37. def SendDMXFrame():
  38. # Re-schedule ourselves in interval ms (do this first to keep the timing consistent)
  39. wrapper.AddEvent(interval, SendDMXFrame)
  40. # Check for new commands
  41. while (True):
  42. try:
  43. cmd = sock.recvfrom(max_mesg_len)[0]
  44. except socket.error:
  45. break
  46. channel, duration, intensity, delay = string.split(cmd, ':')
  47. try:
  48. channel = int(channel)
  49. duration = int(duration)
  50. intensity = int(intensity)
  51. except ValueError:
  52. print 'Invalid command:', cmd
  53. channel = -1
  54. try:
  55. delay = int(delay)
  56. except ValueError:
  57. delay = 0
  58. # Save valid commands
  59. if (channel >= 0 and channel <= 512 and intensity >= 0 and intensity <= 255 and duration >= 0 and duration <= 300000 and delay >= 0 and delay < 300000):
  60. if (channel > 0):
  61. cmds['value'][channel - 1] = intensity
  62. cmds['ticks'][channel - 1] = duration / interval
  63. cmds['delay'][channel - 1] = delay / interval
  64. else:
  65. for i in range(len(state)):
  66. cmds['value'][i] = intensity
  67. cmds['ticks'][i] = duration / interval
  68. cmds['delay'][i] = delay / interval
  69. else:
  70. print 'Invalid command parameters:', channel, duration, intensity, delay
  71. # Update values for each channel
  72. for i in range(len(cmds['value'])):
  73. delta = 0
  74. if (cmds['value'][i] != state[i]):
  75. if (cmds['delay'][i] > 0):
  76. cmds['delay'][i] -= 1
  77. else:
  78. diff = cmds['value'][i] - state[i]
  79. if (cmds['ticks'][i] < 1):
  80. delta = diff
  81. else:
  82. delta = float(diff) / float(cmds['ticks'][i])
  83. state[i] += delta
  84. cmds['ticks'][i] -= 1
  85. if (0):
  86. print 'Channel:', (i + 1)
  87. print "\tDelay:", cmds['delay'][i], "\tValue:", state[i], "\tDelta:", delta, "\tTicks:", cmds['ticks'][i]
  88. # Send all DMX channels
  89. data = array.array('B')
  90. for i in range(len(state)):
  91. data.append(int(state[i]))
  92. wrapper.Client().SendDmx(universe, data, DmxSent)
  93. # ====================================
  94. # Main
  95. # ====================================
  96. # Pick a universe (default from above, or as specified in the environment)
  97. if 'UNIVERSE' in os.environ:
  98. universe = int(os.environ['UNIVERSE'])
  99. # Pick a tick interval (default from above, or as specified in the environment)
  100. if 'INTERVAL' in os.environ:
  101. universe = int(os.environ['INTERVAL'])
  102. # Pick a socket file ($TMPDIR/plexMonitor/DMX.socket, or as specified in the environment)
  103. cmd_file = None
  104. data_dir = None
  105. if 'SOCKET' in os.environ:
  106. cmd_file = os.environ['SOCKET']
  107. data_dir = os.dirname(cmd_file)
  108. else:
  109. proc = subprocess.Popen(['getconf', 'DARWIN_USER_TEMP_DIR'], stdout=subprocess.PIPE, shell=False)
  110. (tmp_dir, err) = proc.communicate()
  111. tmp_dir = tmp_dir.strip()
  112. data_dir = tmp_dir + 'plexMonitor/'
  113. cmd_file = data_dir + 'DMX.socket'
  114. # Sanity checks
  115. if (not os.path.isdir(data_dir)):
  116. raise Exception('Bad config: ' + data_dir)
  117. # Open the socket
  118. if (os.path.exists(cmd_file)):
  119. os.unlink(cmd_file)
  120. sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
  121. sock.bind(cmd_file)
  122. sock.setblocking(0)
  123. # Start the DMX loop
  124. wrapper = ClientWrapper()
  125. wrapper.AddEvent(interval, SendDMXFrame)
  126. wrapper.Run()