Announcement

Collapse
No announcement yet.

Please help me get QLC (Qlight) working.

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Please help me get QLC (Qlight) working.

    So I have been working on this problem for the last 8 hours. I have searched every forum on here and everywhere else. Unfortunately, I don't understand how API POST and GET works all too well.
    .
    I am trying to do something quite simple which is just trigger a single button for QLC. These control the garden lights and this program seems to work the best. I cannot for the life of me figure out how to get homeseer to trigger it.
    .
    The webserver for QLC is 192.168.0.2:9999 for me. On inspecting the element of the button on the webpage of QLC its says ID31 not that I'm even sure it means anything.
    .
    I'd had assumed this website http://www.qlcplus.org/Test_Web_API.html would have helped me but it didn't. The function ID is 48 on this web page. The function type is Scene and currently, function status is "stopped".
    .
    I need some script to trigger the button on but can't find code similar to make it work.
    .
    Also, I have tried used Keyboard pressed to toggle function but this program does not see the presses or react to them. Neither does AutoIT.
    .
    Appreciate any help given. I am struggling with this one.

    #2
    HTTP POST/GET should be relatively easy to get working with some of the existing scripting calls (hs.geturl/hs.urlaction) however it is not clear on that page what the commands actually are - do you have a document listing them?

    Comment


      #3
      Unfortunately, in all the forums I have found I could not find any. I will do some more research into finding them. I have also submitted a post on the QLC forum to see if someone there can help me with the commands.
      .
      Thanks

      Comment


        #4
        Originally posted by shirec View Post
        Unfortunately, in all the forums I have found I could not find any. I will do some more research into finding them. I have also submitted a post on the QLC forum to see if someone there can help me with the commands.
        .
        Thanks
        Do a "view source" on the html page of the local server and post it here. Maybe someone can spot something in it that can be used.

        Cheers
        Al
        HS 4.2.8.0: 2134 Devices 1252 Events
        Z-Wave 3.0.10.0: 133 Nodes on one Z-Net

        Comment


          #5
          Well its not exactly simple but I believe this is the code for the web API and contains the commands in some form.

          I believe this is one of them to get the status. The status I would be getting is for widget 31. It's also 0 = off and 255 = on

          Code:
           <div class="apiButton" onclick="javascript:requestAPIWithParam('getWidgetStatus', 'wStatusID');">getWidgetStatus</div>
              Widget ID:<input id="wStatusID" type="text" value="0">
            </td>
            <td>Retrieve the status of a Virtual Console Widget with the given ID</td>
            <td><div id="getWidgetStatusBox" class="resultBox"></div></td>
           </tr>
          This is the code to change the status to 255 or 0.

          Code:
          <div class="apiButton" onclick="javascript:vcWidgetSetValue('basicWidgetID', 'basicWidgetValue');">Basic widget value set</div>
          
             Widget ID:<input id="basicWidgetID" type="text" value="0">
             Value:<input id="basicWidgetValue" type="text" value="255">
            </td>
            <td colspan="2">
              This API is the direct way to set a Virtual Console widget value. It can be used for Buttons, Sliders and
              Audio Triggers. The value to set depends on the widget type itself. Buttons and Audio triggers will only support values 0 (= off) and 255 (= on) while Sliders will accept all the values in the 0-255 range.
            </td>
          This is the full code as listed on that web page.

          Code:
          <!DOCTYPE html>
          <head>
          <meta http-equiv="content-type" content="text/html; charset=utf-8" />
          <title>QLC+ Web API Test</title>
          <script type="text/javascript">
          // the WebSocket instance
          var websocket;
          var isConnected = false;
          // the websocket host location
          var wshost = "http://127.0.0.1:9999";
          // helper function to send QLC+ API commands
          function requestAPI(cmd) 
          {
            if (isConnected == true)
              websocket.send("QLC+API|" + cmd);
            else
              alert("You must connect to QLC+ WebSocket first !");
          }
          // helper function to send a QLC+ API with one parameter.
          // The specified parameter is not a value, but a CSS object
          // from which a value is retrieved (usually a <input> box)
          function requestAPIWithParam(cmd, paramObjName) 
          {
            var obj = document.getElementById(paramObjName);
            if (obj)
            {
              if (isConnected == true)
                websocket.send("QLC+API|" + cmd + "|" + obj.value);
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function requestChannelsRange(cmd, uniObjName, addressObjName, rangeObjName) 
          {
            var uniObj = document.getElementById(uniObjName);
            var addrObj = document.getElementById(addressObjName);
            var rangeObj = document.getElementById(rangeObjName);
            if (uniObj && addrObj && rangeObj)
            {
              if (isConnected == true)
                websocket.send("QLC+API|" + cmd + "|" + uniObj.value + "|" + addrObj.value + "|" + rangeObj.value);
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function setSimpleDeskChannel(addressObjName, channelValueObjName)
          {
            var addrObj = document.getElementById(addressObjName);
            var valObj = document.getElementById(channelValueObjName);
            if (addrObj && valObj)
            {
              if (isConnected == true)
                websocket.send("CH|" + addrObj.value + "|" + valObj.value);
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function vcWidgetSetValue(wIDObjName, wValueObjName)
          {
            var wObj = document.getElementById(wIDObjName);
            var valObj = document.getElementById(wValueObjName);
            if (wObj && valObj)
            {
              if (isConnected == true)
                websocket.send(wObj.value + "|" + valObj.value);
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function vcCueListControl(clIDObjName, clOpObjName, clStepObjName)
          {
            var clObj = document.getElementById(clIDObjName);
            var opObj = document.getElementById(clOpObjName);
            var stepObj = document.getElementById(clStepObjName);
            if (clObj && opObj)
            {
              if (isConnected == true)
              {
                if (opObj.value == "STEP")
                  websocket.send(clObj.value + "|" + opObj.value + "|" + stepObj.value);
                else
                  websocket.send(clObj.value + "|" + opObj.value);
              }
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function vcFrameControl(frIDObjName, frOperation)
          {
            var frObj = document.getElementById(frIDObjName);
            var opObj = document.getElementById(frOperation);
            if (frObj && opObj)
            {
              if (isConnected == true)
              {
                  websocket.send(frObj.value + "|" + opObj.value);
              }
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
          }
          function connectToWebSocket(host) {
            var url = 'ws://' + host + '/qlcplusWS';
            websocket = new WebSocket(url);
            // update the host information
            wshost = "http://" + host;
            websocket.onopen = function(ev) {
              //alert("QLC+ connection successful");
              document.getElementById('connStatus').innerHTML = "<font color=green>Connected</font>";
              isConnected = true;
            };
            websocket.onclose = function(ev) {
              alert("QLC+ connection lost !");
            };
            websocket.onerror = function(ev) {
              alert("QLC+ connection error!");
            };
           
            // WebSocket message handler. This is where async events
            // will be shown or processed as needed
            websocket.onmessage = function(ev) {
              // Uncomment the following line to display the received message
              //alert(ev.data);
              // Event data is formatted as follows: "QLC+API|API name|arguments"
              // Arguments vary depending on the API called
              var msgParams = ev.data.split('|');
              
              if (msgParams[0] == "QLC+API")
              {
                if (msgParams[1] == "getFunctionsNumber")
          	document.getElementById('getFunctionsNumberBox').innerHTML = msgParams[2];
                
                // Arguments is an array formatted as follows: 
                // Function ID|Function name|Function ID|Function name|...
                else if (msgParams[1] == "getFunctionsList")
                {
          	var tableCode = "<table class='apiTable'><tr><th>ID</th><th>Name</th></tr>";
          	for (i = 2; i < msgParams.length; i+=2)
          	{
          	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td></tr>";
          	}
          	tableCode += "</table>";
          	document.getElementById('getFunctionsListBox').innerHTML = tableCode;
                }
                else if (msgParams[1] == "getFunctionType")
          	document.getElementById('getFunctionTypeBox').innerHTML = msgParams[2];
                else if (msgParams[1] == "getFunctionStatus")
          	document.getElementById('getFunctionStatusBox').innerHTML = msgParams[2];
                else if (msgParams[1] == "getWidgetsNumber")
          	document.getElementById('getWidgetsNumberBox').innerHTML = msgParams[2];
                
                // Arguments is an array formatted as follows: 
                // Widget ID|Widget name|Widget ID|Widget name|...
                else if (msgParams[1] == "getWidgetsList")
                {
          	var tableCode = "<table class='apiTable'><tr><th>ID</th><th>Name</th></tr>";
          	for (i = 2; i < msgParams.length; i+=2)
          	{
          	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td></tr>";
          	}
          	tableCode += "</table>";
          	document.getElementById('getWidgetsListBox').innerHTML = tableCode;
                }
                
                else if (msgParams[1] == "getWidgetType")
          	document.getElementById('getWidgetTypeBox').innerHTML = msgParams[2];
          	
                else if (msgParams[1] == "getWidgetStatus")
                {
          	var status = msgParams[2];
          	if (msgParams[2] == "PLAY")
          	  status = msgParams[2] + "(Step: " + msgParams[3] + ")";
          	document.getElementById('getWidgetStatusBox').innerHTML = status;
                }
                
                else if (msgParams[1] == "getChannelsValues")
                {
          	var tableCode = "<table class='apiTable'><tr><th>Index</th><th>Value</th><th>Type</th></tr>";
          	for (i = 2; i < msgParams.length; i+=3)
          	{
          	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td><td>" + msgParams[i + 2] + "</td></tr>";
          	}
          	tableCode += "</table>";
          	document.getElementById('requestChannelsRangeBox').innerHTML = tableCode;
                }
              }
            };
          };
          function loadProject () {
            var formAction = wshost + "/loadProject";
            document.getElementById('lpForm').action = formAction;
          }
          </script>
          
          <style type="text/css">
          body { 
            background-color: #45484d;
            color: white;
            font:normal 18px/1.2em sans-serif;
          }
          iframe {
            position: absolute;
            display: block;
            height: 100%;
            width: 100%;
            -moz-border-radius: 12px;
            -webkit-border-radius: 12px; 
            border-radius: 12px; 
            -moz-box-shadow: 4px 4px 14px #000; 
            -webkit-box-shadow: 4px 4px 14px #000; 
            box-shadow: 4px 4px 14px #000; 
          }
          #prjBox {
            position: absolute;
            width: 50%;
            height: 70%;
            margin-top: 0px;
            margin-left: 47%;
          }
          .apiTable {
            border-collapse: collapse;
          }
          .apiTable th {
            font-size: 18px;
            color: white;
            border: solid 1px white;
          }
          .apiTable tr {
            font-size: 14px; 
            border: solid 1px white;
          }
          .apiTable td {
            border: solid 1px white;
            padding: 2px 5px 2px 5px;
            margin: 0 5px 0 5px;
          }
          .apiButton {
            display: table-cell; 
            vertical-align: middle;
            text-align: center;
            color: black;
            cursor:pointer;
            height: 30px;
            padding: 0 10px 0 10px;
            background: #4477a1;
            background: -webkit-gradient(linear, left top, left bottom, from(#81a8cb), to(#4477a1) );
            background: -moz-linear-gradient(-90deg, #81a8cb, #4477a1);
            -moz-border-radius: 6px;
            -webkit-border-radius: 6px; 
            border-radius: 6px; 
          }
          .apiButton:hover {
            background: #81a8cb;
            background: -webkit-gradient(linear, left top, left bottom, from(#4477a1), to(#81a8cb) );
            background: -moz-linear-gradient(-90deg, #4477a1, #81a8cb);
          }
          .resultBox {
            display: table-cell;
            vertical-align: middle;
            text-align: center;
            color: #000;
            width: 150px;
            height: 30px;
            background-color: #aaaaaa;
            border-radius: 6px; 
          }
          </style>
          
          <body>
          
          <h2>Q Light Controller+ Web API test page</h2>
          
          <!-- ############## Project box to display what QLC+ is doing ####################### -->
          <div id="prjBox"><iframe name="projectFrame" src="" id="projectFrame"></iframe></div>
          
          <!-- ############## Websocket connection code ####################### -->
          QLC+ IP: 
          <input type="text" id="qlcplusIP" value="127.0.0.1:9999"/>
          <input type="button" value="Connect" onclick="javascript:connectToWebSocket(document.getElementById('qlcplusIP').value);">
          <div id="connStatus" style="display: inline-block;"><font color=red>Not connected</font></div>
          <br><br>
          
          <!-- ############## Project load code ####################### -->
          <form id="lpForm" onsubmit="loadProject()" method="POST" enctype="multipart/form-data" target="projectFrame">
          Load a project:
          <input id="loadTrigger" type="file" onchange="document.getElementById('submitTrigger').click();" name="qlcprj">
          <input id="submitTrigger" type="submit">
          </form>
          <br><br>
          
          <!-- ############## Individual API tests ####################### -->
          
          <table class="apiTable" width=45%>
           <tr>
            <th width=30%><b>API Function</b></th>
            <th width=30%><b>Description</b></th>
            <th width=40%><b>Result</b></th>
           </tr>
           
          <!-- ############## Channels API tests ####################### -->
          
           <tr>
            <td colspan="3" align="center"><b>Channels APIs</b></td>
           </tr>
            <tr>
            <td>
              <div class="apiButton" onclick="javascript:requestChannelsRange('getChannelsValues', 'chUniIdx', 'chDMXaddr', 'chRange');">getChannelsValues</div>
              Universe index:<input id="chUniIdx" type="text" value="1">
              DMX start address:<input id="chDMXaddr" type="text" value="1">
              Channels count:<input id="chRange" type="text" value="16">
            </td>
            <td>Retrieve the specified number of DMX values for the given universe, starting at the given address.
                Note that indices start from 1 and not from 0.</td>
            <td><div id="requestChannelsRangeBox" style="height: 150px; overflow-y: scroll;"></div></td>
           </tr>
          
          <!-- ############## Functions API tests ####################### -->
           
           <tr>
            <td colspan="3" align="center"><b>Function APIs</b></td>
           </tr>
           <tr>
            <td><div class="apiButton" onclick="javascript:requestAPI('getFunctionsNumber');">getFunctionsNumber</div></td>
            <td>Retrieve the number of functions loaded</td>
            <td><div id="getFunctionsNumberBox" class="resultBox"></div></td>
           </tr>
           <tr>
            <td><div class="apiButton" onclick="javascript:requestAPI('getFunctionsList');">getFunctionsList</div></td>
            <td>Retrieve the list of functions with their ID and name</td>
            <td><div id="getFunctionsListBox" style="height: 150px; overflow-y: scroll;"></div></td>
           </tr>
           <tr>
            <td>
              <div class="apiButton" onclick="javascript:requestAPIWithParam('getFunctionType', 'fTypeID');">getFunctionType</div>
              Function ID:<input id="fTypeID" type="text" value="0">
            </td>
            <td>Retrieve the type of a function with the given ID</td>
            <td><div id="getFunctionTypeBox" class="resultBox"></div></td>
           </tr>
           <tr>
            <td>
              <div class="apiButton" onclick="javascript:requestAPIWithParam('getFunctionStatus', 'fStatusID');">getFunctionStatus</div>
              Function ID:<input id="fStatusID" type="text" value="0">
            </td>
            <td>Retrieve the status of a function with the given ID. Possible values are "Running", "Stopped" and "Undefined"</td>
            <td><div id="getFunctionStatusBox" class="resultBox"></div></td>
            </tr>
            
          <!-- ############## Widgets API tests ####################### -->
          
           <tr>
            <td colspan="3" align="center"><b>Virtual Console Widget APIs</b></td>
           </tr>
           <tr>
            <td><div class="apiButton" onclick="javascript:requestAPI('getWidgetsNumber');">getWidgetsNumber</div></td>
            <td>Retrieve the number of widgets loaded</td>
            <td><div id="getWidgetsNumberBox" class="resultBox"></div></td>
           </tr>
           <tr>
            <td><div class="apiButton" onclick="javascript:requestAPI('getWidgetsList');">getWidgetsList</div></td>
            <td>Retrieve the list of Virtual Console Widgets with their ID and name</td>
            <td><div id="getWidgetsListBox" style="height: 150px; overflow-y: scroll;"></div></td>
           </tr>
           <tr>
            <td>
              <div class="apiButton" onclick="javascript:requestAPIWithParam('getWidgetType', 'wTypeID');">getWidgetType</div>
              Widget ID:<input id="wTypeID" type="text" value="0">
            </td>
            <td>Retrieve the type of a Virtual Console Widget with the given ID</td>
            <td><div id="getWidgetTypeBox" class="resultBox"></div></td>
           </tr>
           <tr>
            <td>
              <div class="apiButton" onclick="javascript:requestAPIWithParam('getWidgetStatus', 'wStatusID');">getWidgetStatus</div>
              Widget ID:<input id="wStatusID" type="text" value="0">
            </td>
            <td>Retrieve the status of a Virtual Console Widget with the given ID</td>
            <td><div id="getWidgetStatusBox" class="resultBox"></div></td>
           </tr>
           
          <!-- ############## High rate API tests ####################### -->
          
           <tr>
            <td colspan="3" align="center"><b>High rate APIs</b></td>
           </tr>
           <tr>
            <td colspan="3">Due to the nature of some type of transmissions (for example a slider changing rapidly),
                            there are a few WebSocket operations stripped down to avoid useless overhead of data.<br>
                            So, instead of transmitting every time the "QLC+API|API name" information, direct calls
                            are here used to accomplish fast operations.
            </td>
           </tr>
          
           <tr>
            <td>
             <div class="apiButton" onclick="javascript:setSimpleDeskChannel('sdDMXAddress', 'sdDMXValue');">Simple Desk channel set</div>
          
             Absolute DMX address:<input id="sdDMXAddress" type="text" value="1">
             Value:<input id="sdDMXValue" type="text" value="100">
            </td>
            <td colspan="2">
             This API sets the value of a single channel of the QLC+ Simple Desk. The parameters to send are:<br>
             <b>Absolute DMX address</b>: this is the address of the DMX channel you want to set. It is absolute in the sense
             that the universe information is implicit in the address itself. So for example addresses on the first
             universe will range from 1 to 512, while addresses on the second universe will range from 513 to 1024,
             and so on.<br>
             <b>Value</b>: the value of the DMX channel to set in a range from 0 to 255.
            </td>
           </tr>
          
           <tr>
            <td>
             <div class="apiButton" onclick="javascript:vcWidgetSetValue('basicWidgetID', 'basicWidgetValue');">Basic widget value set</div>
          
             Widget ID:<input id="basicWidgetID" type="text" value="0">
             Value:<input id="basicWidgetValue" type="text" value="255">
            </td>
            <td colspan="2">
              This API is the direct way to set a Virtual Console widget value. It can be used for Buttons, Sliders and
              Audio Triggers. The value to set depends on the widget type itself. Buttons and Audio triggers will only
              support values 0 (= off) and 255 (= on) while Sliders will accept all the values in the 0-255 range.
            </td>
           </tr>
           
           <tr>
            <td>
             <div class="apiButton" onclick="javascript:vcCueListControl('clWidgetID', 'clOperation', 'clStep');">Cue list control</div>
          
             Cue List ID:<input id="clWidgetID" type="text" value="0">
             Operation:<input id="clOperation" type="text" value="PLAY">
             Step (optional):<input id="clStep" type="text" value="1">
            </td>
            <td colspan="2">
              This API demonstrates how to control a Virtual Console Cue List widget. The parameters to be used are:<br>
              <b>Cue List ID</b>: The Cue List widget ID as retrieved with the 'getWidgetsList' API<br>
              <b>Operation</b>: The Cue List operation to perform. Possible values are 'PLAY', 'NEXT', 'PREV' and 'STEP'.
              Only the 'STEP' operation requires a third parameter. The 'PLAY' operation will stop the Cue List if called
              twice.<br>
              <b>Step</b>: The Cue List step index to play. Index starts from 0.
            </td>
           </tr>
          
           <tr>
            <td>
             <div class="apiButton" onclick="javascript:vcFrameControl('frWidgetID', 'frOperation');">Multipage frame control</div>
          
             Frame ID:<input id="frWidgetID" type="text" value="0">
             Operation:<input id="frOperation" type="text" value="NEXT_PG">
            </td>
            <td colspan="2">
              This API demonstrates how to change page of a Virtual Console Frame widget in multipage mode. 
              The parameters to be used are:<br>
              <b>Frame ID</b>: The Frame widget ID as retrieved with the 'getWidgetsList' API<br>
              <b>Operation</b>: The Frame operation to perform. Accepted values are 'NEXT_PG' and 'PREV_PG'.
            </td>
           </tr>
          
          </table>
          
          </body>
          </html>

          Comment


            #6
            This is the source info from the WEB API

            Code:
            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8" />
            <script language="javascript" type="text/javascript">
            
            // the WebSocket instance
            var websocket;
            var isConnected = false;
            // the websocket host location
            var wshost = "http://127.0.0.1:9999";
            
            // helper function to send QLC+ API commands
            function requestAPI(cmd) 
            {
              if (isConnected == true)
                websocket.send("QLC+API|" + cmd);
              else
                alert("You must connect to QLC+ WebSocket first !");
            }
            
            // helper function to send a QLC+ API with one parameter.
            // The specified parameter is not a value, but a CSS object
            // from which a value is retrieved (usually a <input> box)
            function requestAPIWithParam(cmd, paramObjName) 
            {
              var obj = document.getElementById(paramObjName);
              if (obj)
              {
                if (isConnected == true)
                  websocket.send("QLC+API|" + cmd + "|" + obj.value);
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function requestChannelsRange(cmd, uniObjName, addressObjName, rangeObjName) 
            {
              var uniObj = document.getElementById(uniObjName);
              var addrObj = document.getElementById(addressObjName);
              var rangeObj = document.getElementById(rangeObjName);
              if (uniObj && addrObj && rangeObj)
              {
                if (isConnected == true)
                  websocket.send("QLC+API|" + cmd + "|" + uniObj.value + "|" + addrObj.value + "|" + rangeObj.value);
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function setSimpleDeskChannel(addressObjName, channelValueObjName)
            {
              var addrObj = document.getElementById(addressObjName);
              var valObj = document.getElementById(channelValueObjName);
              if (addrObj && valObj)
              {
                if (isConnected == true)
                  websocket.send("CH|" + addrObj.value + "|" + valObj.value);
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function vcWidgetSetValue(wIDObjName, wValueObjName)
            {
              var wObj = document.getElementById(wIDObjName);
              var valObj = document.getElementById(wValueObjName);
              if (wObj && valObj)
              {
                if (isConnected == true)
                  websocket.send(wObj.value + "|" + valObj.value);
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function vcCueListControl(clIDObjName, clOpObjName, clStepObjName)
            {
              var clObj = document.getElementById(clIDObjName);
              var opObj = document.getElementById(clOpObjName);
              var stepObj = document.getElementById(clStepObjName);
              if (clObj && opObj)
              {
                if (isConnected == true)
                {
                  if (opObj.value == "STEP")
                    websocket.send(clObj.value + "|" + opObj.value + "|" + stepObj.value);
                  else
                    websocket.send(clObj.value + "|" + opObj.value);
                }
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function vcFrameControl(frIDObjName, frOperation)
            {
              var frObj = document.getElementById(frIDObjName);
              var opObj = document.getElementById(frOperation);
            
              if (frObj && opObj)
              {
                if (isConnected == true)
                {
                    websocket.send(frObj.value + "|" + opObj.value);
                }
                else
                  alert("You must connect to QLC+ WebSocket first !");
              }
            }
            
            function connectToWebSocket(host) {
              var url = 'ws://' + host + '/qlcplusWS';
              websocket = new WebSocket(url);
              // update the host information
              wshost = "http://" + host;
            
              websocket.onopen = function(ev) {
                //alert("QLC+ connection successful");
                document.getElementById('connStatus').innerHTML = "<font color=green>Connected</font>";
                isConnected = true;
              };
            
              websocket.onclose = function(ev) {
                alert("QLC+ connection lost !");
              };
            
              websocket.onerror = function(ev) {
                alert("QLC+ connection error!");
              };
             
              // WebSocket message handler. This is where async events
              // will be shown or processed as needed
              websocket.onmessage = function(ev) {
                // Uncomment the following line to display the received message
                //alert(ev.data);
            
                // Event data is formatted as follows: "QLC+API|API name|arguments"
                // Arguments vary depending on the API called
            
                var msgParams = ev.data.split('|');
                
                if (msgParams[0] == "QLC+API")
                {
                  if (msgParams[1] == "getFunctionsNumber")
            	document.getElementById('getFunctionsNumberBox').innerHTML = msgParams[2];
                  
                  // Arguments is an array formatted as follows: 
                  // Function ID|Function name|Function ID|Function name|...
                  else if (msgParams[1] == "getFunctionsList")
                  {
            	var tableCode = "<table class='apiTable'><tr><th>ID</th><th>Name</th></tr>";
            	for (i = 2; i < msgParams.length; i+=2)
            	{
            	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td></tr>";
            	}
            	tableCode += "</table>";
            	document.getElementById('getFunctionsListBox').innerHTML = tableCode;
                  }
            
                  else if (msgParams[1] == "getFunctionType")
            	document.getElementById('getFunctionTypeBox').innerHTML = msgParams[2];
            
                  else if (msgParams[1] == "getFunctionStatus")
            	document.getElementById('getFunctionStatusBox').innerHTML = msgParams[2];
            
                  else if (msgParams[1] == "getWidgetsNumber")
            	document.getElementById('getWidgetsNumberBox').innerHTML = msgParams[2];
                  
                  // Arguments is an array formatted as follows: 
                  // Widget ID|Widget name|Widget ID|Widget name|...
                  else if (msgParams[1] == "getWidgetsList")
                  {
            	var tableCode = "<table class='apiTable'><tr><th>ID</th><th>Name</th></tr>";
            	for (i = 2; i < msgParams.length; i+=2)
            	{
            	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td></tr>";
            	}
            	tableCode += "</table>";
            	document.getElementById('getWidgetsListBox').innerHTML = tableCode;
                  }
                  
                  else if (msgParams[1] == "getWidgetType")
            	document.getElementById('getWidgetTypeBox').innerHTML = msgParams[2];
            	
                  else if (msgParams[1] == "getWidgetStatus")
                  {
            	var status = msgParams[2];
            	if (msgParams[2] == "PLAY")
            	  status = msgParams[2] + "(Step: " + msgParams[3] + ")";
            	document.getElementById('getWidgetStatusBox').innerHTML = status;
                  }
                  
                  else if (msgParams[1] == "getChannelsValues")
                  {
            	var tableCode = "<table class='apiTable'><tr><th>Index</th><th>Value</th><th>Type</th></tr>";
            	for (i = 2; i < msgParams.length; i+=3)
            	{
            	  tableCode = tableCode + "<tr><td>" + msgParams[i] + "</td><td>" + msgParams[i + 1] + "</td><td>" + msgParams[i + 2] + "</td></tr>";
            	}
            	tableCode += "</table>";
            	document.getElementById('requestChannelsRangeBox').innerHTML = tableCode;
                  }
                }
              };
            };
            
            function loadProject () {
              var formAction = wshost + "/loadProject";
              document.getElementById('lpForm').action = formAction;
            }
            
            </script>
            
            <style>
            
            body { 
              background-color: #45484d;
              color: white;
              font:normal 18px/1.2em sans-serif;
            }
            
            iframe {
              position: absolute;
              display: block;
            
              height: 100%;
              width: 100%;
            
              -moz-border-radius: 12px;
              -webkit-border-radius: 12px; 
              border-radius: 12px; 
            
              -moz-box-shadow: 4px 4px 14px #000; 
              -webkit-box-shadow: 4px 4px 14px #000; 
              box-shadow: 4px 4px 14px #000; 
            }
            
            #prjBox {
              position: absolute;
              width: 50%;
              height: 70%;
              margin-top: 0px;
              margin-left: 47%;
            }
            
            .apiTable {
              border-collapse: collapse;
            }
            
            .apiTable th {
              font-size: 18px;
              color: white;
              border: solid 1px white;
            }
            
            .apiTable tr {
              font-size: 14px; 
              border: solid 1px white;
            }
            
            .apiTable td {
              border: solid 1px white;
              padding: 2px 5px 2px 5px;
              margin: 0 5px 0 5px;
            }
            
            .apiButton {
              display: table-cell; 
              vertical-align: middle;
              text-align: center;
              color: black;
              cursor:pointer;
              height: 30px;
              padding: 0 10px 0 10px;
              background: #4477a1;
              background: -webkit-gradient(linear, left top, left bottom, from(#81a8cb), to(#4477a1) );
              background: -moz-linear-gradient(-90deg, #81a8cb, #4477a1);
            
              -moz-border-radius: 6px;
              -webkit-border-radius: 6px; 
              border-radius: 6px; 
            }
            
            .apiButton:hover {
              background: #81a8cb;
              background: -webkit-gradient(linear, left top, left bottom, from(#4477a1), to(#81a8cb) );
              background: -moz-linear-gradient(-90deg, #4477a1, #81a8cb);
            }
            
            .resultBox {
              display: table-cell;
              vertical-align: middle;
              text-align: center;
              color: #000;
              width: 150px;
              height: 30px;
              background-color: #aaaaaa;
              border-radius: 6px; 
            }
            
            </style>
            
            <body>
            
            <h2>Q Light Controller+ Web API test page</h2>
            
            <!-- ############## Project box to display what QLC+ is doing ####################### -->
            <div id="prjBox"><iframe name="projectFrame" src="" id="projectFrame"></iframe></div>
            
            <!-- ############## Websocket connection code ####################### -->
            QLC+ IP: 
            <input type="text" id="qlcplusIP" value="127.0.0.1:9999"></input>
            <input type="button" value="Connect" onclick="javascript:connectToWebSocket(document.getElementById('qlcplusIP').value);"></input>
            <div id="connStatus" style="display: inline-block;"><font color=red>Not connected</font></div>
            <br><br>
            
            <!-- ############## Project load code ####################### -->
            <form id="lpForm" onsubmit="loadProject()" method="POST" enctype="multipart/form-data" target="projectFrame">
            Load a project:
            <input id="loadTrigger" type="file" onchange="document.getElementById('submitTrigger').click();" name="qlcprj" />
            <input id="submitTrigger" type="submit" />
            </form>
            <br><br>
            
            <!-- ############## Individual API tests ####################### -->
            
            <table class="apiTable" width=45%>
             <tr>
              <th width=30%><b>API Function</b></th>
              <th width=30%><b>Description</b></th>
              <th width=40%><b>Result</b></th>
             </tr>
             
            <!-- ############## Channels API tests ####################### -->
            
             <tr>
              <td colspan="3" align="center"><b>Channels APIs</b></td>
             </tr>
              <tr>
              <td>
                <div class="apiButton" onclick="javascript:requestChannelsRange('getChannelsValues', 'chUniIdx', 'chDMXaddr', 'chRange');">getChannelsValues</div>
                Universe index:<input id="chUniIdx" type="input" value="1"></input>
                DMX start address:<input id="chDMXaddr" type="input" value="1"></input>
                Channels count:<input id="chRange" type="input" value="16"></input>
              </td>
              <td>Retrieve the specified number of DMX values for the given universe, starting at the given address.
                  Note that indices start from 1 and not from 0.</td>
              <td><div id="requestChannelsRangeBox" style="height: 150px; overflow-y: scroll;"></div></td>
             </tr>
            
            <!-- ############## Functions API tests ####################### -->
             
             <tr>
              <td colspan="3" align="center"><b>Function APIs</b></td>
             </tr>
             <tr>
              <td><div class="apiButton" onclick="javascript:requestAPI('getFunctionsNumber');">getFunctionsNumber</div></td>
              <td>Retrieve the number of functions loaded</td>
              <td><div id="getFunctionsNumberBox" class="resultBox"></div></td>
             </tr>
             <tr>
              <td><div class="apiButton" onclick="javascript:requestAPI('getFunctionsList');">getFunctionsList</div></td>
              <td>Retrieve the list of functions with their ID and name</td>
              <td><div id="getFunctionsListBox" style="height: 150px; overflow-y: scroll;"></div></td>
             </tr>
             <tr>
              <td>
                <div class="apiButton" onclick="javascript:requestAPIWithParam('getFunctionType', 'fTypeID');">getFunctionType</div>
                Function ID:<input id="fTypeID" type="input" value="0"></input>
              </td>
              <td>Retrieve the type of a function with the given ID</td>
              <td><div id="getFunctionTypeBox" class="resultBox"></div></td>
             </tr>
             <tr>
              <td>
                <div class="apiButton" onclick="javascript:requestAPIWithParam('getFunctionStatus', 'fStatusID');">getFunctionStatus</div>
                Function ID:<input id="fStatusID" type="input" value="0"></input>
              </td>
              <td>Retrieve the status of a function with the given ID. Possible values are "Running", "Stopped" and "Undefined"</td>
              <td><div id="getFunctionStatusBox" class="resultBox"></div></td>
              </tr>
              
            <!-- ############## Widgets API tests ####################### -->
            
             <tr>
              <td colspan="3" align="center"><b>Virtual Console Widget APIs</b></td>
             </tr>
             <tr>
              <td><div class="apiButton" onclick="javascript:requestAPI('getWidgetsNumber');">getWidgetsNumber</div></td>
              <td>Retrieve the number of widgets loaded</td>
              <td><div id="getWidgetsNumberBox" class="resultBox"></div></td>
             </tr>
             <tr>
              <td><div class="apiButton" onclick="javascript:requestAPI('getWidgetsList');">getWidgetsList</div></td>
              <td>Retrieve the list of Virtual Console Widgets with their ID and name</td>
              <td><div id="getWidgetsListBox" style="height: 150px; overflow-y: scroll;"></div></td>
             </tr>
             <tr>
              <td>
                <div class="apiButton" onclick="javascript:requestAPIWithParam('getWidgetType', 'wTypeID');">getWidgetType</div>
                Widget ID:<input id="wTypeID" type="input" value="0"></input>
              </td>
              <td>Retrieve the type of a Virtual Console Widget with the given ID</td>
              <td><div id="getWidgetTypeBox" class="resultBox"></div></td>
             </tr>
             <tr>
              <td>
                <div class="apiButton" onclick="javascript:requestAPIWithParam('getWidgetStatus', 'wStatusID');">getWidgetStatus</div>
                Widget ID:<input id="wStatusID" type="input" value="0"></input>
              </td>
              <td>Retrieve the status of a Virtual Console Widget with the given ID</td>
              <td><div id="getWidgetStatusBox" class="resultBox"></div></td>
             </tr>
             
            <!-- ############## High rate API tests ####################### -->
            
             <tr>
              <td colspan="3" align="center"><b>High rate APIs</b></td>
             </tr>
             <tr>
              <td colspan="3">Due to the nature of some type of transmissions (for example a slider changing rapidly),
                              there are a few WebSocket operations stripped down to avoid useless overhead of data.<br>
                              So, instead of transmitting every time the "QLC+API|API name" information, direct calls
                              are here used to accomplish fast operations.
              </td>
             </tr>
            
             <tr>
              <td>
               <div class="apiButton" onclick="javascript:setSimpleDeskChannel('sdDMXAddress', 'sdDMXValue');">Simple Desk channel set</div>
            
               Absolute DMX address:<input id="sdDMXAddress" type="input" value="1"></input>
               Value:<input id="sdDMXValue" type="input" value="100"></input>
              </td>
              <td colspan="2">
               This API sets the value of a single channel of the QLC+ Simple Desk. The parameters to send are:<br>
               <b>Absolute DMX address</b>: this is the address of the DMX channel you want to set. It is absolute in the sense
               that the universe information is implicit in the address itself. So for example addresses on the first
               universe will range from 1 to 512, while addresses on the second universe will range from 513 to 1024,
               and so on.<br>
               <b>Value</b>: the value of the DMX channel to set in a range from 0 to 255.
              </td>
             </tr>
            
             <tr>
              <td>
               <div class="apiButton" onclick="javascript:vcWidgetSetValue('basicWidgetID', 'basicWidgetValue');">Basic widget value set</div>
            
               Widget ID:<input id="basicWidgetID" type="input" value="0"></input>
               Value:<input id="basicWidgetValue" type="input" value="255"></input>
              </td>
              <td colspan="2">
                This API is the direct way to set a Virtual Console widget value. It can be used for Buttons, Sliders and
                Audio Triggers. The value to set depends on the widget type itself. Buttons and Audio triggers will only
                support values 0 (= off) and 255 (= on) while Sliders will accept all the values in the 0-255 range.
              </td>
             </tr>
             
             <tr>
              <td>
               <div class="apiButton" onclick="javascript:vcCueListControl('clWidgetID', 'clOperation', 'clStep');">Cue list control</div>
            
               Cue List ID:<input id="clWidgetID" type="input" value="0"></input>
               Operation:<input id="clOperation" type="input" value="PLAY"></input>
               Step (optional):<input id="clStep" type="input" value="1"></input>
              </td>
              <td colspan="2">
                This API demonstrates how to control a Virtual Console Cue List widget. The parameters to be used are:<br>
                <b>Cue List ID</b>: The Cue List widget ID as retrieved with the 'getWidgetsList' API<br>
                <b>Operation</b>: The Cue List operation to perform. Possible values are 'PLAY', 'NEXT', 'PREV' and 'STEP'.
                Only the 'STEP' operation requires a third parameter. The 'PLAY' operation will stop the Cue List if called
                twice.<br>
                <b>Step</b>: The Cue List step index to play. Index starts from 0.
              </td>
             </tr>
            
             <tr>
              <td>
               <div class="apiButton" onclick="javascript:vcFrameControl('frWidgetID', 'frOperation');">Multipage frame control</div>
            
               Frame ID:<input id="frWidgetID" type="input" value="0"></input>
               Operation:<input id="frOperation" type="input" value="NEXT_PG"></input>
              </td>
              <td colspan="2">
                This API demonstrates how to change page of a Virtual Console Frame widget in multipage mode. 
                The parameters to be used are:<br>
                <b>Frame ID</b>: The Frame widget ID as retrieved with the 'getWidgetsList' API<br>
                <b>Operation</b>: The Frame operation to perform. Accepted values are 'NEXT_PG' and 'PREV_PG'.
              </td>
             </tr>
            
            </table>
            
            </body>
            </html>


            Code:
            <!DOCTYPE html>
            <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8" >
            <title>QLC+ Webaccess</title>
            <link href="common.css" rel="stylesheet" type="text/css" media="screen">
            <link href="virtualconsole.css" rel="stylesheet" type="text/css" media="screen">
            <script type="text/javascript" src="websocket.js"></script>
            <script type="text/javascript" src="virtualconsole.js"></script>
            <script type="text/javascript">
            framesWidth[0] = 590;
            framesHeight[0] = 270;
            framesWidth[1] = 190;
            framesHeight[1] = 100;
            framesWidth[7] = 650;
            framesHeight[7] = 330;
            framesWidth[8] = 250;
            framesHeight[8] = 280;
            framesWidth[27] = 1080;
            framesHeight[27] = 865;
            
            </script>
            </head>
            <body>
            <form action="/loadProject" method="POST" enctype="multipart/form-data">
            <input id="loadTrigger" type="file" onchange="document.getElementById('submitTrigger').click();" name="qlcprj" />
            <input id="submitTrigger" type="submit"/>
            </form>
            <div class="controlBar">
            <a class="button button-blue" href="javascript:document.getElementById('loadTrigger').click();">
            <span>Load project</span></a>
            <a class="button button-blue" href="/simpleDesk"><span>Simple Desk</span></a>
            <a class="button button-blue" href="/config"><span>Configuration</span></a>
            <div class="swInfo">Q Light Controller Plus 4.10.5b</div></div>
            <div style="position: relative; width: 1920px; height: 1080px; background-color: #f0f0f0;">
            <div class="vcframe" id="fr0" style="left: 1330px; top: 0px; width: 590px; height: 270px; background-color: #f0f0f0; border: 1px solid #5a5a5a;">
            <a class="vcframeButton" style="position: absolute; left: 0; z-index: 1;" href="javascript:frameToggleCollapse(0);"><img src="expand.png" width="27"></a>
            <div class="vcframeHeader" style="color:#ffffff;"><div class="vcFrameText">Primary colours - Generic RGB</div></div>
            <div class="vcsoloframe" id="fr1" style="left: 10px; top: 40px; width: 190px; height: 100px; background-color: #f0f0f0; border: 1px solid #ff0000;">
            <a class="vcframeButton" style="position: absolute; left: 0; z-index: 1;" href="javascript:frameToggleCollapse(1);"><img src="expand.png" width="27"></a>
            <div class="vcsoloframeHeader" style="color:#ffffff;"><div class="vcFrameText">Presets solo frame</div></div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 40px;">
            <a class="vcbutton" id="2" href="javascript:buttonClick(2);" style="width: 50px; height: 50px; color: #000000; background-color: #ff0000; ">Red scene</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 40px;">
            <a class="vcbutton" id="3" href="javascript:buttonClick(3);" style="width: 50px; height: 50px; color: #000000; background-color: #00ff00; ">Green scene</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 40px;">
            <a class="vcbutton" id="4" href="javascript:buttonClick(4);" style="width: 50px; height: 50px; color: #000000; background-color: #0000ff; ">Blue scene</a>
            </div>
            </div>
            <div id="5" class="vccuelist" style="left: 210px; top: 40px; width: 300px; height: 220px; background-color: #f0f0f0;">
            <div style="width: 100%; height: 186px; overflow: scroll;" >
            <table class="hovertable" style="width: 100%;">
            <tr><th>#</th><th>Name</th><th>Fade In</th><th>Fade Out</th><th>Duration</th><th>Notes</th></tr>
            <tr id="5_0" onclick="enableCue(5, 0);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(5, 0);">
            <td>1</td><td>Red scene - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="5_1" onclick="enableCue(5, 1);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(5, 1);">
            <td>2</td><td>Green scene - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="5_2" onclick="enableCue(5, 2);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(5, 2);">
            <td>3</td><td>Blue scene - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            </table>
            </div>
            <a class="vccuelistButton" id="play5" href="javascript:sendCueCmd(5, 'PLAY');">
            <img src="player_play.png" width="27"></a>
            <a class="vccuelistButton" id="stop5" href="javascript:sendCueCmd(5, 'STOP');">
            <img src="player_stop.png" width="27"></a>
            <a class="vccuelistButton" href="javascript:sendCueCmd(5, 'PREV');">
            <img src="back.png" width="27"></a>
            <a class="vccuelistButton" href="javascript:sendCueCmd(5, 'NEXT');">
            <img src="forward.png" width="27"></a>
            </div>
            <div class="vcslider" style="left: 520px; top: 40px; width: 60px; height: 200px; background-color: #f0f0f0;">
            <div id="slv6" class="vcslLabel" style="top:0px;">0</div>
            <input type="range" class="vVertical" id="6" oninput="slVchange(6);" ontouchmove="slVchange(6);" style="width: 150px; margin-top: 150px; margin-left: 30px;" min="0" max="255" step="1" value="0">
            <div id="sln6" class="vcslLabel" style="bottom:0px;">Click & Go RGB</div>
            </div>
            </div>
            <div class="vcframe" id="fr7" style="left: 1265px; top: 290px; width: 650px; height: 330px; background-color: #f0f0f0; border: 1px solid #5a5a5a;">
            <a class="vcframeButton" style="position: absolute; left: 0; z-index: 1;" href="javascript:frameToggleCollapse(7);"><img src="expand.png" width="27"></a>
            <div class="vcframeHeader" style="color:#ffffff;"><div class="vcFrameText">16 Colours - Generic RGB</div></div>
            <div class="vcsoloframe" id="fr8" style="left: 10px; top: 40px; width: 250px; height: 280px; background-color: #f0f0f0; border: 1px solid #ff0000;">
            <a class="vcframeButton" style="position: absolute; left: 0; z-index: 1;" href="javascript:frameToggleCollapse(8);"><img src="expand.png" width="27"></a>
            <div class="vcsoloframeHeader" style="color:#ffffff;"><div class="vcFrameText">Presets solo frame</div></div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 40px;">
            <a class="vcbutton" id="10" href="javascript:buttonClick(10);" style="width: 50px; height: 50px; color: #000000; background-color: #000080; ">Scene Dark Blue</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 40px;">
            <a class="vcbutton" id="11" href="javascript:buttonClick(11);" style="width: 50px; height: 50px; color: #000000; background-color: #0000ff; ">Scene Blue</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 40px;">
            <a class="vcbutton" id="12" href="javascript:buttonClick(12);" style="width: 50px; height: 50px; color: #000000; background-color: #008000; ">Scene Dark Green</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 100px;">
            <a class="vcbutton" id="13" href="javascript:buttonClick(13);" style="width: 50px; height: 50px; color: #000000; background-color: #008080; ">Scene Dark Cyan</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 100px;">
            <a class="vcbutton" id="14" href="javascript:buttonClick(14);" style="width: 50px; height: 50px; color: #000000; background-color: #00ff00; ">Scene Green</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 100px;">
            <a class="vcbutton" id="15" href="javascript:buttonClick(15);" style="width: 50px; height: 50px; color: #000000; background-color: #00ffff; ">Scene Cyan</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 100px;">
            <a class="vcbutton" id="16" href="javascript:buttonClick(16);" style="width: 50px; height: 50px; color: #000000; background-color: #800000; ">Scene Dark Red</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 160px;">
            <a class="vcbutton" id="17" href="javascript:buttonClick(17);" style="width: 50px; height: 50px; color: #000000; background-color: #800080; ">Scene Dark Magenta</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 160px;">
            <a class="vcbutton" id="18" href="javascript:buttonClick(18);" style="width: 50px; height: 50px; color: #000000; background-color: #808000; ">Scene Dark Yellow</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 160px;">
            <a class="vcbutton" id="19" href="javascript:buttonClick(19);" style="width: 50px; height: 50px; color: #000000; background-color: #808080; ">Scene Dark Gray</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 160px;">
            <a class="vcbutton" id="20" href="javascript:buttonClick(20);" style="width: 50px; height: 50px; color: #000000; background-color: #c0c0c0; ">Scene Light Gray</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 220px;">
            <a class="vcbutton" id="21" href="javascript:buttonClick(21);" style="width: 50px; height: 50px; color: #000000; background-color: #ff0000; ">Scene Red</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 220px;">
            <a class="vcbutton" id="22" href="javascript:buttonClick(22);" style="width: 50px; height: 50px; color: #000000; background-color: #ff00ff; ">Scene Magenta</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 220px;">
            <a class="vcbutton" id="23" href="javascript:buttonClick(23);" style="width: 50px; height: 50px; color: #000000; background-color: #ffff00; ">Scene Yellow</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 220px;">
            <a class="vcbutton" id="24" href="javascript:buttonClick(24);" style="width: 50px; height: 50px; color: #000000; background-color: #ffffff; ">Scene White</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 40px;">
            <a class="vcbutton" id="9" href="javascript:buttonClick(9);" style="width: 50px; height: 50px; color: #000000; background-color: #000000; ">Scene Black</a>
            </div>
            </div>
            <div id="25" class="vccuelist" style="left: 270px; top: 40px; width: 300px; height: 220px; background-color: #f0f0f0;">
            <div style="width: 100%; height: 186px; overflow: scroll;" >
            <table class="hovertable" style="width: 100%;">
            <tr><th>#</th><th>Name</th><th>Fade In</th><th>Fade Out</th><th>Duration</th><th>Notes</th></tr>
            <tr id="25_0" onclick="enableCue(25, 0);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 0);">
            <td>1</td><td>Scene Black - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_1" onclick="enableCue(25, 1);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 1);">
            <td>2</td><td>Scene Dark Blue - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_2" onclick="enableCue(25, 2);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 2);">
            <td>3</td><td>Scene Blue - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_3" onclick="enableCue(25, 3);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 3);">
            <td>4</td><td>Scene Dark Green - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_4" onclick="enableCue(25, 4);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 4);">
            <td>5</td><td>Scene Dark Cyan - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_5" onclick="enableCue(25, 5);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 5);">
            <td>6</td><td>Scene Green - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_6" onclick="enableCue(25, 6);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 6);">
            <td>7</td><td>Scene Cyan - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_7" onclick="enableCue(25, 7);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 7);">
            <td>8</td><td>Scene Dark Red - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_8" onclick="enableCue(25, 8);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 8);">
            <td>9</td><td>Scene Dark Magenta - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_9" onclick="enableCue(25, 9);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 9);">
            <td>10</td><td>Scene Dark Yellow - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_10" onclick="enableCue(25, 10);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 10);">
            <td>11</td><td>Scene Dark Gray - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_11" onclick="enableCue(25, 11);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 11);">
            <td>12</td><td>Scene Light Gray - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_12" onclick="enableCue(25, 12);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 12);">
            <td>13</td><td>Scene Red - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_13" onclick="enableCue(25, 13);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 13);">
            <td>14</td><td>Scene Magenta - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_14" onclick="enableCue(25, 14);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 14);">
            <td>15</td><td>Scene Yellow - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            <tr id="25_15" onclick="enableCue(25, 15);" onmouseover="this.style.backgroundColor='#CCD9FF';" onmouseout="checkMouseOut(25, 15);">
            <td>16</td><td>Scene White - Generic RGB</td><td>3s</td><td>0ms</td><td>10s</td><td></td>
            </td>
            </table>
            </div>
            <a class="vccuelistButton" id="play25" href="javascript:sendCueCmd(25, 'PLAY');">
            <img src="player_play.png" width="27"></a>
            <a class="vccuelistButton" id="stop25" href="javascript:sendCueCmd(25, 'STOP');">
            <img src="player_stop.png" width="27"></a>
            <a class="vccuelistButton" href="javascript:sendCueCmd(25, 'PREV');">
            <img src="back.png" width="27"></a>
            <a class="vccuelistButton" href="javascript:sendCueCmd(25, 'NEXT');">
            <img src="forward.png" width="27"></a>
            </div>
            <div class="vcslider" style="left: 580px; top: 40px; width: 60px; height: 200px; background-color: #f0f0f0;">
            <div id="slv26" class="vcslLabel" style="top:0px;">0</div>
            <input type="range" class="vVertical" id="26" oninput="slVchange(26);" ontouchmove="slVchange(26);" style="width: 150px; margin-top: 150px; margin-left: 30px;" min="0" max="255" step="1" value="0">
            <div id="sln26" class="vcslLabel" style="bottom:0px;">Click & Go RGB</div>
            </div>
            </div>
            <div class="vcsoloframe" id="fr27" style="left: 180px; top: 10px; width: 1080px; height: 865px; background-color: #f0f0f0; border: 1px solid #ff0000;">
            <a class="vcframeButton" style="position: absolute; left: 0; z-index: 1;" href="javascript:frameToggleCollapse(27);"><img src="expand.png" width="27"></a>
            <div class="vcsoloframeHeader" style="color:#ffffff;"><div class="vcFrameText">Animations - Generic RGB</div></div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 40px;">
            <a class="vcbutton" id="28" href="javascript:buttonClick(28);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">abstract</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 40px;">
            <a class="vcbutton" id="29" href="javascript:buttonClick(29);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">ocean</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 40px;">
            <a class="vcbutton" id="30" href="javascript:buttonClick(30);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">rainbow</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 280px; top: 55px;">
            <a class="vcbutton" id="31" href="javascript:buttonClick(31);" style="width: 300px; height: 255px; color: #000000; background-color: #f0f0f0; border: 3px solid #00E600;">sunset</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 100px;">
            <a class="vcbutton" id="32" href="javascript:buttonClick(32);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Warm White</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 100px;">
            <a class="vcbutton" id="33" href="javascript:buttonClick(33);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">yellow</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 100px;">
            <a class="vcbutton" id="34" href="javascript:buttonClick(34);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">green</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 100px;">
            <a class="vcbutton" id="35" href="javascript:buttonClick(35);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">white</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 160px;">
            <a class="vcbutton" id="36" href="javascript:buttonClick(36);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Flash test red purple</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 160px;">
            <a class="vcbutton" id="37" href="javascript:buttonClick(37);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Random flashing</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 160px;">
            <a class="vcbutton" id="38" href="javascript:buttonClick(38);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">random flashing red green</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 160px;">
            <a class="vcbutton" id="39" href="javascript:buttonClick(39);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Opposite</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 220px;">
            <a class="vcbutton" id="40" href="javascript:buttonClick(40);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Plasma</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 220px;">
            <a class="vcbutton" id="41" href="javascript:buttonClick(41);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Plasma (Colors)</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 220px;">
            <a class="vcbutton" id="42" href="javascript:buttonClick(42);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Column</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 220px;">
            <a class="vcbutton" id="43" href="javascript:buttonClick(43);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Fill Column</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 280px;">
            <a class="vcbutton" id="44" href="javascript:buttonClick(44);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Fill Row</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 280px;">
            <a class="vcbutton" id="45" href="javascript:buttonClick(45);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Fill Single</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 280px;">
            <a class="vcbutton" id="46" href="javascript:buttonClick(46);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Row</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 280px;">
            <a class="vcbutton" id="47" href="javascript:buttonClick(47);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Random Single</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 340px;">
            <a class="vcbutton" id="48" href="javascript:buttonClick(48);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Squares</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 340px;">
            <a class="vcbutton" id="49" href="javascript:buttonClick(49);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Squares From Center</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 340px;">
            <a class="vcbutton" id="50" href="javascript:buttonClick(50);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Stripes</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 190px; top: 340px;">
            <a class="vcbutton" id="51" href="javascript:buttonClick(51);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Stripes From Center</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 10px; top: 400px;">
            <a class="vcbutton" id="52" href="javascript:buttonClick(52);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Strobe</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 70px; top: 400px;">
            <a class="vcbutton" id="53" href="javascript:buttonClick(53);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Vertical fall</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 130px; top: 400px;">
            <a class="vcbutton" id="54" href="javascript:buttonClick(54);" style="width: 50px; height: 50px; color: #000000; background-color: #f0f0f0; ">Animation Waves</a>
            </div>
            <div class="vcbutton-wrapper" style="left: 720px; top: 65px;">
            <a class="vcbutton" id="55" href="javascript:buttonClick(55);" style="width: 295px; height: 230px; color: #000000; background-color: #000000; ">Scene Black</a>
            </div>
            </div>
            </div>
            </body>
            </html>
            Last edited by shirec; February 15, 2017, 06:52 AM. Reason: updated source list

            Comment


              #7
              Im sure it's close to this.. but not this.

              It has to be close to this but its saying its expecting more information. I am sure this is not enough to send a command and am missing something... The above code says a websocket must be opened first...


              Code:
              dim s
              const server_url = "192.168.0.2:9999"
              const headers="Content-Type: text/html; charset=utf-8"  
              
              s = hs.URLAction(server_url, "POST", input id="31", type="text", value="0", headers)

              Comment


                #8
                Looks like the webpage references a websocket.js file. Take a look at it as it may give more info as to what it's doing on the backend.

                Cheers
                Al
                HS 4.2.8.0: 2134 Devices 1252 Events
                Z-Wave 3.0.10.0: 133 Nodes on one Z-Net

                Comment


                  #9
                  I'd personally look if at all possible away from websockets which this appears to use, theres no native support built into HS for them (they are a protocol in there own right). Whilst there is a System.Net.Sockets namespace that only works with .net 4.5 and HS is written for .net 4.0 so they won't work out of the box. I'd approach the QLight people and see if they have a HTTP API...

                  Comment


                    #10
                    I just asked if they have a http API in there forum. Its a free program not a paid one so I am going to assume there is no http API and nobody will go out of there way to make it happen.
                    .
                    This is unfortunate as my garden lights are controlled by DMX. Does anyone have any recommendations for dmx controlled lighting which can be affected by Homeseer.
                    .
                    Maby I will try and find a program similar to Qlight that does support http API? Or does Homeseer support DMX natively? (Through E1.31 protocol?)
                    .
                    I am after any solution to get the lights working. It needs to be more than just solid colours tho.
                    .
                    Thanks

                    Comment


                      #11
                      Originally posted by shirec View Post
                      I just asked if they have a http API in there forum. Its a free program not a paid one so I am going to assume there is no http API and nobody will go out of there way to make it happen.
                      .
                      This is unfortunate as my garden lights are controlled by DMX. Does anyone have any recommendations for dmx controlled lighting which can be affected by Homeseer.
                      .
                      Maby I will try and find a program similar to Qlight that does support http API? Or does Homeseer support DMX natively? (Through E1.31 protocol?)
                      .
                      I am after any solution to get the lights working. It needs to be more than just solid colours tho.
                      .
                      Thanks
                      Have you seen this - https://forums.homeseer.com/showthread.php?t=184921 - I used FreeStyler and control it from HS script.

                      Comment


                        #12
                        Appreciate that. If I can get it to do some simple rgb colour wave then it would definitely work. I will look into it very soon! Sounds good tho. Thanks again!

                        Comment


                          #13
                          I'm looking into making a quick HomeSeer plugin for QLC+. Interessted?
                          HSPro 3.0.0.458, Z-NET with Z-wave plugin 3.0.1.190, RFXCOM + 2x RFXtrx433E, HSTouch, Squeezebox plugin, iTach IP/WF2IR & GC-100-6 with UltraGCIR, BLDenon, NetcamStudio, Jon00s Webpage builder, Harmony Hub plugin, SCSIP (with FreePBX), Arduino plugin, IFTTT, Pushalot plugin, Device History plugin.
                          Running on Windows 10 (64) virtualized
                          on ESXi (Fujitsu Primergy TX150 S8).
                          WinSeer (for Win10) - TextSeer - FitbitSeer - HSPI_MoskusSample

                          Are you Norwegian (or Scandinavian) and getting started with HomeSeer? Read the "HomeSeer School"!

                          Comment


                            #14
                            Originally posted by Moskus View Post
                            I'm looking into making a quick HomeSeer plugin for QLC+. Interessted?
                            I would be interested. I am gluing those two together at the moment and would like to to do it properly. (Rather than a Harmony IR keyboard hack I was playing with).

                            What is your approach? A Homeseer plugin, a QLC+ plugin or a couple of flows in NodeRed (let it handle the protocols)?

                            I am not a coder, but can test, provide ideas, cheer from the sidelines, etc.

                            Comment


                              #15
                              Solved it.. it was easy!

                              I managed to get everything working just fine together without line of code... or any cost. So far it has been remarkably reliable. I used what I already had lying around and once I had selected the right approach (of 4 bad attempts) it all worked great.

                              Just thought I would put together a detailed guide if here, for others.

                              What I already had:
                              - HS3 with the Dzjee MQTT plugin.
                              - An MQTT server running on my HS3 box... highly reliable, zero performance impact I have noticed. Zero installation issues and virtually no configuration.

                              Aside: I was using this to control a bunch of cheap SONOFF wi-fi controllers. Again remarkably good value and quality for the price. Pretty much my device of choice now for mains switching. How to hack them and make them MQTT compatible here. These are a good starter for fiddling with MQTT... All you do it setup a bunch of manually triggered EVENTS in HS3 for sending MQTT messages like this:
                              Click image for larger version

Name:	MQTTExample1.PNG
Views:	1
Size:	30.7 KB
ID:	1194652
                              Setup a set of those and you are done...if you want to get clever and setup virtual devices which can change state based on MQTT messages I guess you can, but I never needed to in my application.

                              I realise that this pair meant I had an easy bi-directional interface to a very open and flexible message bus, so the HS3 side is already sorted... I just needed to get from MQTT messages to QLC+.

                              That can be provided by Node Red (again another 2 minute install). I have this as a Docker on my Synology server, but I could have used the one on the Raspberry PI I have QLC+ running on, or anywhere else. Again it is very low impact server and very flexible. You could easily install NodeRed and MQTT on any Raspberry PI... I think they are both already included in a full PI install anyway.

                              Then use the built in MQTT & Websocket nodes to build a very simple flow like this:
                              Click image for larger version

Name:	NodeRed1.jpg
Views:	1
Size:	36.0 KB
ID:	1194654

                              The Nodes are trivial to configure and were done as follows:
                              Click image for larger version

Name:	NodeRed2.PNG
Views:	1
Size:	30.4 KB
ID:	1194655
                              The "Topic" there is entirely made up - just in line with the SONOFF stuff previously to give a semblance of structure. The topic is there so NodeRed knows which MQTT messages to pick up and pass to QLC+.

                              Click image for larger version

Name:	NodeRed3.PNG
Views:	1
Size:	27.5 KB
ID:	1194656
                              This just points at the MQTT Server.

                              The Inject commands are there for manual selection and debugging, but they are useful as they show the command:
                              Click image for larger version

Name:	NodeRed4.PNG
Views:	1
Size:	27.3 KB
ID:	1194657

                              All you need to do is work out the command to send... this is where you use the Q Light Controller+ Web API test page. Just connect to your local QLC+ server and then hit the "getWidgetsList" button to list all the buttons setup on your virtual console.
                              Click image for larger version

Name:	QLC-API.jpg
Views:	1
Size:	30.6 KB
ID:	1194658
                              The value that is passed is the Widget#|0 for off & Widget#|255 for on.
                              These buttons I have set up in the virtual console in QLC+ which just trigger the scene. I have all the buttons set up in a Solo Frame, so that QLC+ deals with all of the switching off automatically.

                              ...continued in next post to get more attachments

                              Comment

                              Working...
                              X