<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -8,24 +8,70 @@ import os, sys, imp, subprocess, curses, curses.ascii, curses.wrapper
 datapath   = None
 data       = None
 dbodata    = {}
+binddata   = {}
 chandata   = {}
 activename = ''
+dmxcommands = {'get' : 'k8062 [channel]',
+               'set' : 'k8062 [channel] [value]'}
 
 # ----- Function Definitions ----- #
+def printhelp():
+    print(&quot;Usage: pydmx (help | [model] [startchannel])&quot;)
+    print(&quot;The model and startchannel are optional, if not specified use the new command.&quot;)
+    print(&quot;PyDMX also accepts commands from stdin, just make sure you end with an exit command to avoid an error message.\n&quot;)
+    print(&quot;Internal commands:&quot;)
+    print(&quot;    new [name] [model] [startchannel]  - initialise a new named device.&quot;)
+    print(&quot;    names                              - list all devices.&quot;)
+    print(&quot;    change [name]                      - change to a different named device.&quot;)
+    print(&quot;    rename [old] [new]                 - change the name of a device. If the active device is renamed, a change command must immediately follow.&quot;)
+    print(&quot;    bind [name] [chan] [name] [chan]   - always have the two channel values equal.&quot;)
+    print(&quot;    unbind [name] [chan] [name] [chan] - undo a bind command.&quot;)
+    print(&quot;    reload                             - reload cached DMX values for the current device from dmxd.&quot;)
+    print(&quot;    curses                             - start the ncurses interface.&quot;)
+    print(&quot;    list (arg)                         - list all commands for the current device provided by the data file. If arg is specified, restrict to that category.&quot;)
+    print(&quot;    dbo                                - toggle dead black out.&quot;)
+    print(&quot;    dmxgetcmd [command]                - set the DMX get command (default: k8062 [channel]).&quot;)
+    print(&quot;    dmxsetcmd [command]                - set the DMX set command (default: k8062 [channel] [value]).&quot;)
+    print(&quot;    dmxcommands                        - print the current DMX commands.&quot;)
+    print(&quot;    help                               - display this text&quot;)
+    print(&quot;    exit                               - close pydmx\n&quot;)
+    print(&quot;Curses UI:&quot;)
+    print(&quot;    Up        - increment the value of the current channel.&quot;)
+    print(&quot;    Down      - decrement the value of the current channel.&quot;)
+    print(&quot;    Left      - change to the previous channel.&quot;)
+    print(&quot;    Right     - change to the next channel.&quot;)
+    print(&quot;    Page Up   - increment the value of the current channel by 10.&quot;)
+    print(&quot;    Page Down - decrement the value of the current channel by 10.&quot;)
+    print(&quot;    n         - change to the next discrete region for that channel.&quot;)
+    print(&quot;    p         - change to the previous discrete region for that channel.&quot;)
+    print(&quot;    t         - change the value of the channel to 255.&quot;)
+    print(&quot;    b         - change the value of the channel to 0.&quot;)
+    print(&quot;    h         - toggle hold mode. Any changes made in hold more are applied when hold mode is disabled.&quot;)
+    print(&quot;    d         - equivalent of dbo command.&quot;)
+    print(&quot;    r         - equivalent of reload command.&quot;)
+    print(&quot;    q         - close the interface.\n&quot;)
+    print(&quot;PyDMX requires that dmxd be running as root, and that k8062 be executable and in your $PATH.&quot;)
+    print(&quot;PyDMX is developed and maintained by Barrucadu (Michael Walker).&quot;)
+
 def dmxcmd(channel, value=None, name=None, holding=False):
     global activename
     global chandata
-
+    global bindddata
+    global dmxcommands
+    
     if name == None:
         name = activename
 
     argvector = []
     if value == None:
-        argvector = [&quot;k8062&quot;, str(channel)]
+        argvector = dmxcommands['get'].replace('[channel]', str(channel)).split()
     else:
         chandata[name][channel][0] = int(value)
-        channel += chandata[name]['info']['startchan']
-        argvector = [&quot;k8062&quot;, str(channel), str(value)]
+        argvector = dmxcommands['set'].replace('[channel]', str(channel)).replace('[value]', str(value)).split()
+
+        if name in binddata.keys():
+            for binded in binddata[(name, channel)]:
+                dmxcmd(binded[1], value, binded[0], holding)
 
     if not holding:
         process = subprocess.Popen(argvector, stdout=subprocess.PIPE)
@@ -64,7 +110,7 @@ def dbo():
     global chandata
     global dbodata
 
-    if not dbodata = {}:
+    if not dbodata == {}:
         for channel in dbodata.keys():
             dmxcmd(channel, dbodata[channel])
         dbodata = {}
@@ -209,7 +255,6 @@ def updateui(stdscr, active, holding):
                         highest = chan
 
             stdscr.addstr(highest[1])
-        
 
 def cursesui(stdscr):
     global chandata
@@ -316,10 +361,12 @@ def listcmds(arg):
                 print(&quot;    &quot; + cmd[0], cmd[1].rjust(len(cmd[1]) + spacing))
             print()
             
-def parsecmd(command, arg, arg2, arg3):
+def parsecmd(command, args):
     global data
     global chandata
     global activename
+    global binddata
+    global dmxcommands
     
     funbreak = False
 
@@ -338,66 +385,64 @@ def parsecmd(command, arg, arg2, arg3):
             print(&quot;    Start:&quot;, chandata[name]['info']['startchan'])
             
     elif command == &quot;new&quot;:
-        if arg == &quot;&quot; or arg2 == &quot;&quot; or arg3 == &quot;&quot;:
+        if len(args) &lt; 3 or args[0] == &quot;&quot; or args[1] == &quot;&quot; or args[2] == &quot;&quot;:
             print(&quot;Usage: new [name] [model] [startchannel]&quot;)
         else:
-            nameinit(arg, arg2, arg3)
+            nameinit(args[0], args[1], args[2])
 
     elif command == &quot;change&quot;:
-        if not arg == &quot;&quot; and arg in chandata.keys():
-            activename = arg
-        else:
+        if len(args) &lt; 1 or args[0] == &quot;&quot; or not args[0] in chandata.keys():
             print(&quot;Usage: change [name]&quot;)
+        else:
+            activename = args[0]
 
     elif command == &quot;reload&quot;:
         loaddmx(activename)
         
     elif command == &quot;rename&quot;:
-        if arg in chandata.keys() and not (arg2 in chandata.keys() or arg2 == &quot;&quot;):
-            chandata.update({arg2 : chandata[arg]})
-            chandata.pop(arg)
-        else:
+        if len(args) &lt; 2 or not args[0] in chandata.keys() or not args[1] in chandata.keys() or args[1] == &quot;&quot;:
             print(&quot;Usage: rename [old] [new]&quot;)
+        else:
+            chandata.update({args[1] : chandata[args[0]]})
+            chandata.pop(args[0])
 
     elif command == &quot;dbo&quot;:
         dbo()
 
+    elif command == &quot;bind&quot;:
+        if (args[0], args[1]) in binddata.keys():
+            binddata[(args[0], args[1])].append((args[2], args[3]))
+        else:
+            binddata.update({(args[0], args[1]) : [(args[2], args[3])]})
+        
+        if (args[2], args[3]) in binddata.keys():
+            binddata[(args[2], args[3])].append((args[0], args[1]))
+        else:
+            binddata.update({(args[2], args[3]) : [(args[0], args[1])]})
+
+    elif command == &quot;unbind&quot;:
+        if (args[0], args[1]) in binddata.keys():
+            binddata[(args[0], args[1])].remove((args[2], args[3]))
+        
+        if (args[2], args[3]) in binddata.keys():
+            binddata[(args[2], args[3])].remove((args[0], args[1]))
+
+    elif command == &quot;dmxsetcmd&quot;:
+        dmxcommands['set'] = ' '.join(args)
+
+    elif command == &quot;dmxgetcmd&quot;:
+        dmxcommands['get'] = ' '.join(args)        
+
+    elif command == &quot;dmxcommands&quot;:
+        print(&quot;Get:&quot;, dmxcommands['get'])
+        print(&quot;Set:&quot;, dmxcommands['set'])
+
     elif command == &quot;help&quot;:
-        print(&quot;Usage: pydmx (help | [model] [startchannel])&quot;)
-        print(&quot;The model and startchannel are optional, if not specified use the new command.&quot;)
-        print(&quot;PyDMX also accepts commands from stdin, just make sure you end with an exit command to avoid an error message.\n&quot;)
-        print(&quot;Internal commands:&quot;)
-        print(&quot;    new [name] [model] [startchannel] - initialise a new named device.&quot;)
-        print(&quot;    names                             - list all devices.&quot;)
-        print(&quot;    change [name]                     - change to a different named device.&quot;)
-        print(&quot;    rename [old] [new]                - change the name of a device. If the active device is renamed, a change command must immediately follow.&quot;)
-        print(&quot;    reload                            - reload cached DMX values for the current device from dmxd.&quot;)
-        print(&quot;    curses                            - start the ncurses interface.&quot;)
-        print(&quot;    list (arg)                        - list all commands for the current device provided by the data file. If arg is specified, restrict to that category.&quot;)
-        print(&quot;    dbo                               - Toggle dead black out.&quot;)
-        print(&quot;    help                              - display this text&quot;)
-        print(&quot;    exit                              - close pydmx\n&quot;)
-        print(&quot;Curses UI:&quot;)
-        print(&quot;    Up        - Increment the value of the current channel.&quot;)
-        print(&quot;    Down      - Decrement the value of the current channel.&quot;)
-        print(&quot;    Left      - Change to the previous channel.&quot;)
-        print(&quot;    Right     - Change to the next channel.&quot;)
-        print(&quot;    Page Up   - Increment the value of the current channel by 10.&quot;)
-        print(&quot;    Page Down - Decrement the value of the current channel by 10.&quot;)
-        print(&quot;    n         - Change to the next discrete region for that channel.&quot;)
-        print(&quot;    p         - Change to the previous discrete region for that channel.&quot;)
-        print(&quot;    t         - Change the value of the channel to 255.&quot;)
-        print(&quot;    b         - Change the value of the channel to 0.&quot;)
-        print(&quot;    h         - Toggle hold mode. Any changes made in hold more are applied when hold mode is disabled.&quot;)
-        print(&quot;    d         - Equivalent of dbo command.&quot;)
-        print(&quot;    r         - Equivalent of reload command.&quot;)
-        print(&quot;    q         - Close the interface.\n&quot;)
-        print(&quot;PyDMX requires that dmxd be running as root, and that k8062 be executable and in your $PATH.&quot;)
-        print(&quot;PyDMX is developed and maintained by Barrucadu (Michael Walker).&quot;)
+        printhelp()
         
     else:
         try:
-            cmd = data.dmx[command][arg]
+            cmd = data.dmx[command][args[0]]
         except KeyError:
             print(&quot;Command not found&quot;)
             funbreak = True
@@ -405,7 +450,7 @@ def parsecmd(command, arg, arg2, arg3):
         if not funbreak:
             value = 0
             try:
-                value = int(arg2)
+                value = int(args[1])
             except ValueError:
                 value = cmd[0]
                 
@@ -414,26 +459,16 @@ def parsecmd(command, arg, arg2, arg3):
 def parsepromptline(line):
     bar      = line.split()
     command  = &quot;&quot;
-    arg      = &quot;&quot;
-    arg2     = &quot;&quot;
-    arg3     = &quot;&quot;
+    args     = []
     funbreak = False
 
     try:
         command = bar[0]
-        
-        if len(bar) &gt; 1:
-            arg = bar[1]
-            
-        if len(bar) &gt; 2:
-            arg2 = bar[2]
-
-        if len(bar) &gt; 3:
-            arg3 = bar[3]
+        args    = bar[1:]
     except IndexError:
         funbreak = True
 
-    return command, arg, arg2, arg3, funbreak
+    return command, args, funbreak
 
 
 def enterprompt():
@@ -444,9 +479,9 @@ def enterprompt():
     if foo == &quot;exit&quot;: 
         return False
     else:
-        command, arg, arg2, arg3, funbreak = parsepromptline(foo)
+        command, args, funbreak = parsepromptline(foo)
         if not funbreak:
-            parsecmd(command, arg, arg2, arg3)
+            parsecmd(command, args)
     return True
 
 # ----- Main ----- #
@@ -455,7 +490,7 @@ if datapath == &quot;&quot;:
     datapath = os.getenv(&quot;HOME&quot;) + &quot;/.local/share&quot;
 
 if &quot;help&quot; in sys.argv or &quot;-h&quot; in sys.argv or &quot;--help&quot; in sys.argv:
-    parsecmd(&quot;help&quot;, None, None, None)
+    printhelp()
     sys.exit(0)
 else:
     if len(sys.argv) &gt; 2:</diff>
      <filename>bin/pydmx</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>de50a726f9450b534b22def8c55db003d5fa9319</id>
    </parent>
  </parents>
  <author>
    <name>Barrucadu</name>
    <email>mike@barrucadu.co.uk</email>
  </author>
  <url>http://github.com/Barrucadu/home/commit/184a95d47a1c7f7ef8ae262a56448ece9228f4d5</url>
  <id>184a95d47a1c7f7ef8ae262a56448ece9228f4d5</id>
  <committed-date>2009-11-08T03:26:48-08:00</committed-date>
  <authored-date>2009-11-08T03:26:48-08:00</authored-date>
  <message>Bind/unbind of channels, and the ability to change the commands used to get/set DMX values. Thus, pydmx should now work with all Linux-compatible DMX interfaces.</message>
  <tree>0082c76557468d94eef82fac9cd1b4d827432a47</tree>
  <committer>
    <name>Barrucadu</name>
    <email>mike@barrucadu.co.uk</email>
  </committer>
</commit>
