dmx.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. ready = select.select([sock], [], [], interval / 2000)[0]
  42. for fh in ready:
  43. cmd = fh.recvfrom(max_mesg_len)[0]
  44. channel, duration, intensity, delay = string.split(cmd, ':')
  45. try:
  46. channel = int(channel)
  47. duration = int(duration)
  48. intensity = int(intensity)
  49. except ValueError:
  50. print 'Invalid command:', cmd
  51. channel = -1
  52. try:
  53. delay = int(delay)
  54. except ValueError:
  55. delay = 0
  56. # Save valid commands
  57. if (channel >= 0 and channel <= 512 and intensity >= 0 and intensity <= 255 and duration >= 0 and duration <= 300000 and delay >= 0 and delay < 300000):
  58. if (channel > 0):
  59. cmds['value'][channel - 1] = intensity
  60. cmds['ticks'][channel - 1] = duration / interval
  61. cmds['delay'][channel - 1] = delay / interval
  62. else:
  63. for i in range(len(state)):
  64. cmds['value'][i] = intensity
  65. cmds['ticks'][i] = duration / interval
  66. cmds['delay'][i] = delay / interval
  67. else:
  68. print 'Invalid command parameters:', channel, duration, intensity, delay
  69. # Update values for each channel
  70. for i in range(len(cmds['value'])):
  71. delta = 0
  72. if (cmds['value'][i] != state[i]):
  73. if (cmds['delay'][i] > 0):
  74. cmds['delay'][i] -= 1
  75. else:
  76. diff = cmds['value'][i] - state[i]
  77. if (cmds['ticks'][i] < 1):
  78. delta = diff
  79. else:
  80. delta = float(diff) / float(cmds['ticks'][i])
  81. state[i] += delta
  82. cmds['ticks'][i] -= 1
  83. if (0):
  84. print 'Channel:', (i + 1)
  85. print "\tDelay:", cmds['delay'][i], "\tValue:", state[i], "\tDelta:", delta, "\tTicks:", cmds['ticks'][i]
  86. # Send all DMX channels
  87. data = array.array('B')
  88. for i in range(len(state)):
  89. data.append(int(state[i]))
  90. wrapper.Client().SendDmx(universe, data, DmxSent)
  91. # ====================================
  92. # Main
  93. # ====================================
  94. # Pick a universe (default from above, or as specified in the environment)
  95. if 'UNIVERSE' in os.environ:
  96. universe = int(os.environ['UNIVERSE'])
  97. # Pick a tick interval (default from above, or as specified in the environment)
  98. if 'INTERVAL' in os.environ:
  99. universe = int(os.environ['INTERVAL'])
  100. # Pick a socket file ($TMPDIR/plexMonitor/DMX.socket, or as specified in the environment)
  101. cmd_file = None
  102. data_dir = None
  103. if 'SOCKET' in os.environ:
  104. cmd_file = os.environ['SOCKET']
  105. data_dir = os.dirname(cmd_file)
  106. else:
  107. proc = subprocess.Popen(['getconf', 'DARWIN_USER_TEMP_DIR'], stdout=subprocess.PIPE, shell=False)
  108. (tmp_dir, err) = proc.communicate()
  109. tmp_dir = tmp_dir.strip()
  110. data_dir = tmp_dir + 'plexMonitor/'
  111. cmd_file = data_dir + 'DMX.socket'
  112. # Sanity checks
  113. if (not os.path.isdir(data_dir)):
  114. raise Exception('Bad config: ' + data_dir)
  115. # Open the socket
  116. if (os.path.exists(cmd_file)):
  117. os.unlink(cmd_file)
  118. sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
  119. sock.bind(cmd_file)
  120. # Start the DMX loop
  121. wrapper = ClientWrapper()
  122. wrapper.AddEvent(interval, SendDMXFrame)
  123. wrapper.Run()