Your Universal Remote Control Center
RemoteCentral.com
Philips Pronto Professional Forum - View Post
Previous section Next section Up level
Up level
The following page was printed from RemoteCentral.com:

Login:
Pass:
 
 

Topic:
Function calls not running synchronously
This thread has 10 replies. Displaying all posts.
Post 1 made on Thursday June 9, 2022 at 23:02
randman
Long Time Member
Joined:
Posts:
June 2003
416
In my Pronto TSU9400, I have a button whose action calls a function in a Javascript library. Let's call this function A. Function A calls another function in another Javascript library. Let's call this function B.

Function A starts up my Home Theater equipment (processor, projector, etc.). Function A has logic to wait for the projector to start up, which takes about a minute. During this minute, function A also displays a "Please Wait" dialog box using GUI.addPanel(). Function B is used to turn on and off lights in the room. The lights are turned on and off using com.philips.HttpLibrary.getHTTP(), which is used to send commands to a server that is responsible for turning on and off lights.

Function A takes a minute to run, and it calls Function B about 6 times during this minute. For example, Function A does:

- Call Function B to turn off the task lights
- Wait 10 seconds or so
- Call Function B to turn on the Ambience lights
- Wait 20 seconds or so
- Call Function B to turn off all lights
- Wait 20 seconds or so
- Turn on step lights
... etc....

What I am puzzled about is that Function B doesn't seem to actually run at the times Function A calls it. The behavior that I am getting is that all the calls to Function B seem to be running after Function A has completed. All the calls to Function B are run one right after the other, instead of being spaced apart like in my list above.

Is there something in the way the Pronto works, such that getHTTP() calls (called by Function B) are queued up and don't run until after the calling function (function A) completes?
Post 2 made on Thursday June 9, 2022 at 23:11
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,992
Please don't take my answer here as a flame.

The example code you are using is worth 10000 of your words describing it. Please post the code.

Since you are using the Philips HTTP Library, then yes, everything is asynchronous. Calling the library will cause a socket to be opened which will process data asynchronously. To make things synchronous you'd need to mimic the code in the Philips library and when your HTTP Request return the final Ready State and a 200 OK, then you would want to start the next task or use scheduleAfter to schedule the next task after some time (a task queue).

How are you doing this delay of Wait 20 seconds or so?

If you are using System.delay, then you should really see this in the Dev Guide.

delay() Wait for a specific time. This blocks script execution during
the specified time.


If you are doing this, then the Pronto is waiting 20 seconds but while it is waiting, it is not doing any of the socket processing in the HttpLibrary.

Sytem.delay(20000) should be avoided at ALL COSTS.

Like I said, the code is worth 10000 words.

Lyndel
Lyndel McGee
Philips Pronto Addict/Beta Tester
OP | Post 3 made on Friday June 10, 2022 at 10:26
randman
Long Time Member
Joined:
Posts:
June 2003
416
Within a for loop, I call:
GUI.updateScreen();
System.delay(1000);

So in order to wait 20 seconds, GUI.updateScreen() and System.delay(1000) are called 20 times. But even if I did System.delay(20000) (which I don't), the getHTTP() call to control lights is called before System.delay, so I would think that getHTTP still would have issued the command to the server before System.delay is called.

Anyway, below is some of the code that I use.

// createWaitPopup() function
// This function creates a popup dialog box that goes over the existing
// window. This function is used in conjunction with the updateWaitPopup()
// and deleteWaitPopup() functions.
// The caller of this function must call deleteWaitPopup() later.
function createWaitPopup()
{
   waitWidgets[0] = GUI.addPanel();
   waitWidgets[0].setImage(CF.widget("PLEASE_WAIT_BACK", "Please Wait Widgets", "Resources").getImage());
   waitWidgets[0].width = 240;
   waitWidgets[0].height = 284;
   waitWidgets[0].top = 36;
   waitWidgets[0].left = 0;
   waitWidgets[0].visible = true;

   waitWidgets[1] = GUI.addPanel();
   waitWidgets[1].setImage(CF.widget("PLEASE_WAIT", "Please Wait Widgets", "Resources").getImage());
   waitWidgets[1].width = 157;
   waitWidgets[1].height = 105;
   waitWidgets[1].top = 110;
   waitWidgets[1].left = 43;
   waitWidgets[1].color = 255;
   waitWidgets[1].font = "ProntoMaestro";
   waitWidgets[1].fontSize = 16;
   waitWidgets[1].visible = true;
} // createWaitPopup



/////////////////////////////////////////////////////////////////////////////
// updateWaitPopup() function
// This function updates the popup window created by the createWaitPopup()
// function defined earlier. The window will display % complete based on
// the input parameters.
// Input parameters:
//    waitDuration: duration in seconds for the wait
//    minPcnt: starting percentage to display
//    maxPcnt: Ending percentage to display
//    message: Optional message to display
// Assumption: if this function is called more than once, the
// minPcnt of a call should be >= maxPcnt of previous call.
// Example use:
// createWaitPopup();
// // Send some remote commands to turn on some components
// updateWaitPopup(50, 0, 25, "Doing job #1");
// // Send more remote commands to turn on other components
// updateWaitPopup(150, 26, 100, "Doing job #2");
// deleteWaitPopup();
// Note: trace() below just calls System.print() if the given trace level is
// below a globally defined trace threshold.
// 10/2019: made changes to ensure that input parameters are treated
// as integers. Otherwise, an exception might be thrown.
function updateWaitPopup(waitDuration_, minPcnt_, maxPcnt_, optionalMessage)
{
   var deltaPcnt, displayPcnt, labelMessage, waitDuration;
   var waitDuration, minPcnt, maxPcnt;
   waitDuration = Math.round(waitDuration_);
   minPcnt = Math.round(minPcnt_);
   // 10/22/19: Account for potential rounding issues:
   if ( (maxPcnt_ > 100.0) && (maxPcnt_ < 101.0) ) {
      maxPcnt = 100;
   } else {
      maxPcnt = Math.round(maxPcnt_);
   }
   trace("minPcnt = " + minPcnt + " maxPcnt = " + maxPcnt + ".", 3);
   if ( (maxPcnt > 100) || (minPcnt < 0) || (maxPcnt <= minPcnt) |
   (waitDuration <= 0) ) {
      throw new Error("updateWaitPopup: ERROR. invalid input parameter.");
   }
   deltaPcnt = (maxPcnt - minPcnt) / waitDuration;
   displayPcnt = minPcnt;
   for (i = 1; i <= waitDuration; i++)
   {
      displayPcnt += deltaPcnt;
      if (optionalMessage.length > 0) {
         labelMessage = "\n\n" + optionalMessage + "\n";
         waitWidgets[1] .fontSize = 14;
      }
      else {
         labelMessage = "\n\n";
         waitWidgets[1] .fontSize = 16;
      }
      waitWidgets[1].label = labelMessage + Math.round(displayPcnt) + "% Done";
      GUI.updateScreen();
      System.delay(1000);
   }
   return;
} // updateWaitPopup

/////////////////////////////////////////////////////////////////////////////
// deleteWaitPopup() function
// This function is used to delete the widgets created by deleteWaitPopup().
function deleteWaitPopup()
{
   var len = waitWidgets.length;
   for (var i = 0; i < len; i++) {
      var w = waitWidgets[i];
      if (w) {
         w.remove();
      }
      waitWidgets[i] = null;
   }
}

/////////////////////////////////////////////////////////////////////////////
// doWaitPopup() function
// Convenience function.
function doWaitPopup(waitDuration, optionalMessage) {
   createWaitPopup();
   updateWaitPopup(waitDuration, 0, 100, optionalMessage);
   deleteWaitPopup();
}


Below is the function that I use to control (turn on or off) lights. The server
that getHTTP() talks to is a HomeSeer server that can turn on or off the lights (aside: HomeSeer talks to Homebridge, which talks to HomeKit to control the lights).

/////////////////////////////////////////////////////////////////////////////
// controlDeviceByLabel(ref, label)
// controlEviceByLabel() is a public function for controlling a device.
// ref is the HomeSeer reference number.
// label is the command to send, typically "On" or "Off".
// When HomeSeer replies, the processResponse2 function is called
// asynchronously.
/////////////////////////////////////////////////////////////////////////////
function controlDeviceByLabel(ref, label)
{
   var msg = "", msgSuffix = "";

   if ( (!ref) || (ref.length == 0) ) {
      com.randman.utils.trace("controlDeviceByLabel: ERROR: empty ref passed in.", 0);
      return false;
   }

   if ( (!label) || (label.length == 0) ) {
      com.randman.utils.trace("controlDeviceByLabel: ERROR: empty label passed in.", 0);
      return false;
   }

   // Sample URL to send to HomeSeer:
   // [Link: ]
   // Use encodeURI in case label has spaces:
   msgSuffix = "controldevicebylabel&ref=" + ref + "&label=" + encodeURI(label);
   msg = HomeSeer.msgPrefix + msgSuffix;
   com.randman.utils.trace("controlDeviceByLabel: msgSuffix = " + msgSuffix + ".", 5);
   HomeSeer.lastMessageSent = msgSuffix;
   com.philips.HttpLibrary.getHTTP(msg, processResponse2);
} // controlDeviceByLabel



////////////////////////////////////////////////////////////////////////////
// processResponse2(rcvd)
// This function is the callback which is called when HomeSeer responds to
// an HTTP request from the controlDeviceByLabel function.
////////////////////////////////////////////////////////////////////////////
function processResponse2(rcvd)
{
   com.randman.utils.trace("processResponse2: response was: " + rcvd + ". Length = " + rcvd.length + ".", 3);
   // For controlDeviceByLabel response, if event was run successfully,
   // HomeSeer returns a JSON formatted status of the device (same
   // return as "getstatus").

   // If an error occurred (for example, an invalid ref or label value was
   // given), HomeSeer will return:
   //
   // { "Response":"Error, controlling device" }
   //
   if (rcvd.length > 22) {
      // Remove "\r\n" at the end of the string
      HomeSeer.lastResponseReceived = rcvd.substr(14, 5);
   }
   else {
      com.randman.utils.trace("Warning: length of string received from HomeSeer <= 22. Length = " + rcvd.length, 0);
      HomeSeer.lastResponseReceived = rcvd;
   }

   if (HomeSeer.lastResponseReceived == "Error") {
      com.randman.utils.trace("Error: " + HomeSeer.lastMessageSent + " failed. Response: " + rcvd, 0);
   }
   else {
      com.randman.utils.trace("Command succeeded: " + HomeSeer.lastMessageSent + ".", 3);
   }
} // processResponse2

/////////////////////////////////////////////////////////////////////////////
// runEvent(groupName, eventName)
// runEvent() is a public function for running a HomeSeer event.
/////////////////////////////////////////////////////////////////////////////
function runEvent(groupName, eventName)
{
   var msg = "", msgSuffix = "";

   if ( (!groupName) || (groupName.length == 0) ) {
      com.randman.utils.trace("runEvent: ERROR: empty groupName passed in.", 0);
      return false;
   }

   if ( (!eventName) || (eventName.length == 0) ) {
      com.randman.utils.trace("runEvent: ERROR: empty eventName passed in.", 0);
      return false;
   }

   // Use encodeURI in case groupName or eventName have spaces:
   msgSuffix = "runevent&group=" + encodeURI(groupName) + "&name=" + encodeURI(eventName);
   msg = HomeSeer.msgPrefix + msgSuffix;
   com.randman.utils.trace("runEvent: msgSuffix = " + msgSuffix + ".", 5);
   HomeSeer.lastMessageSent = msgSuffix;
   com.philips.HttpLibrary.getHTTP(msg, processResponse);
} // runEvent

////////////////////////////////////////////////////////////////////////////
// processResponse(rcvd)
// This function is the callback which is called when HomeSeer responds to
// an HTTP request from the runEvent function.
////////////////////////////////////////////////////////////////////////////
function processResponse(rcvd)
{
   com.randman.utils.trace("processResponse: response was: " + rcvd + ". Length = " + rcvd.length + ".", 3);
   // For runEvent response, if event was run successfully, HomeSeer
   // returns "ok". Otherwise, HomeSeer will return "error" (for example,
   // if an invalid group or event name was given.
   // Sometime after 3/20/2015, HomeSeer
   // changed response from ok to { "Response":"ok" }
   if (rcvd.length >= 16) {
      HomeSeer.lastResponseReceived = rcvd.substr(14, 2);
   }
   else if (rcvd.length > 2) {
      // Remove "\r\n" at the end of the string
      HomeSeer.lastResponseReceived = rcvd.substr(0, rcvd.length - 2);
   }
   else {
      com.randman.utils.trace("Warning: length of string received from HomeSeer <= 2. Length = " + rcvd.length, 0);
      HomeSeer.lastResponseReceived = rcvd;
   }

   if (HomeSeer.lastResponseReceived == "ok") {
      com.randman.utils.trace("Command " + HomeSeer.lastMessageSent + " completed successfully.", 3);
   }
   else {
      com.randman.utils.trace("ERROR. Command failed: " + HomeSeer.lastMessageSent + ".", 0);
   }

} // processResponse



Below is code that I use whenever an activity (Apple TV, Shield, DVD Player), etc. is selected by the user. It checks if the processor and projector need to be turned on. If so, it turns them on and waits a certain amount of time for them to power on before proceeding. The projector takes a minute to turn on, so during this minute, I'd like to do a light show (turn off ceiling lights, turn on ambiance lights, turn off ambiance lights, etc.).

The interesting part of the function is in this if block:

if (HTProjectorState != "On") {
... turn on projector
... while waiting for projector to turn on, do a light show (using getHTTP
... commands while telling the user to please wait.
}

In this function below, none of the HomeSeer runEvent (used to call HomeSeer events via getHTTP()) and controlDeviceByLabel calls (used to tell HomeSeer to turn on or off lights using getHTTP()) in this function turn on and off the lights until AT THE END OR COMPLETION of this function. The behavior is as if all the runEvent and controlDeviceByLabel function calls are done one right after the other but only after this function completes. I don't know why they don't actually turn on or off the lights run at the time they are called. The lights only turn on and off after the minute it takes the projector to turn on. It seems that
Pronto queues up the getHTTP requiests and doesn't run them until the function completes. There is also no delay between when all the lights turn on and off, which seems to show that all getHTTP commands are running one right after the next. The lights don't start turning on and off until after the function finishes.

   function HTSystemOn(activity) {
         var HTPreampState, HTProjectorState, rc = 2, message;
         com.randman.utils.createWaitPopup();
         // getPreampState() and getHTProjectorState() uses RS232
         // and RFX9600 to get the current states of the processor
         // and projector.
         HTPreampState = com.randman.marantz_preamp.getPreampState();
         HTProjectorState = com.randman.jvc_projector.getHTProjectorState();
       
         // The wait* variables below are how long the function waits
         // using the updateWaitPopup() function.
         // waitAmbianceLights and waitSpeakerLights is how
         // long the ambiance and speaker lights should be ON while the
         // projector is starting, respectively.
         // NOTE: waitAmbianceLights + waitSpeakerLights must be <= waitProjectorOn.
         var waitPreampOn = 4, waitPreampHDMIOutput = 18, waitProjectorOn = 55, waitSwitchInput = 6, waitAmbianceLights = 40, waitSpeakerLights = 10, waitProjectorOnRemaining, waitTotal = 0, minPcnt = 0, maxPcnt = 0;
         // Calculate total wait time so we can correctly show
         // the user the % done in the wait dialog box.
         if (HTPreampState == "Off") {
            waitTotal += waitPreampOn;
            if (HTProjectorState == "On") {
               waitTotal += waitPreampHDMIOutput;
            }
         }

         if (HTProjectorState != "On") {
            waitTotal += waitProjectorOn;
         }

         if (HTPreampState == "Off") {
            // Need to turn on the Marantz processor
            rc = 1;
            try {
               minPcnt = maxPcnt + 1;
               maxPcnt = maxPcnt + 100 * waitPreampOn/waitTotal;
               com.randman.utils.updateWaitPopup(waitPreampOn, minPcnt, maxPcnt, "Powering Preamp");
            }
            catch(e2) {
               com.randman.utils.deleteWaitPopup();
               com.randman.utils.trace(e2, 0);
               message = "Could not turn on preamp.\nTry again.";
               com.randman.utils.createPopup(message);
               return 0;
            }
            // Switch the preamp's input based on activity:
            if (HTSwitchInput(1, activity) == 0) {
               com.randman.utils.deleteWaitPopup();
               message = "Could not switch input to\n" + activity + ". Try again.";
               com.randman.utils.createPopup(message);
               return 0;
            }
            try {
               // Processor can send output to projector or monitor/TV (or both).
               // Tell processor to send output to projector only:
               com.randman.utils.trace("Changing preamp HDMI output to projector.", 1);
               CF.widget("Preamp Output - Projector", "HT", "PS_MACROS").executeActions();

            }
            catch(e) {
               com.randman.utils.deleteWaitPopup();
               com.randman.utils.trace(e, 0);
               message = "Could not set preamp output to\nprojector. Try again.";
               com.randman.utils.createPopup(message);
               return 0;
            }
            if (HTProjectorState == "On") {
               // Only do this wait if projector is already on.
               minPcnt = maxPcnt + 1;
               maxPcnt = maxPcnt + 100 * waitPreampHDMIOutput/waitTotal;
               com.randman.utils.updateWaitPopup(waitPreampHDMIOutput, minPcnt, maxPcnt, "HDMI Out->projector" + activity);
            }
         }
         else {
            // Preamp already on
            var HTSIRC;
            HTSIRC = HTSwitchInput(2, activity);
            if (HTSIRC == 0) {
               com.randman.utils.deleteWaitPopup();
               message = "Unable to switch input to\n" + activity + ". Try again.";
               com.randman.utils.createPopup(message);
               return 0;
            }
            // Need to tell user to wait since it takes a while for
            // the JVC projector to show a video when input resolution changes.
            // However, we do not want to bring up dialog box if we
            // are already on the correct input to begin with.
            if ( (HTSIRC == 1) && (HTProjectorState == "On") ) {
               // Given this condition, this is the only time where we
               // need to tell the user to wait.
               com.randman.utils.updateWaitPopup(waitSwitchInput, 1, 100,
                  "Prepping " + activity);
            }
            // If the Home Theater was being used with the TV (and the
            // preamp was on), and the user selects an activity for the
            // projector, the preamp's HDMI Monitor output would be
            // for the TV and not the projector. Need to change to projector.
            if (HTProjectorState == "Off") {
               try {
                  com.randman.utils.trace("2. Changing preamp HDMI output to projector.", 1);
                  CF.widget("Preamp Output - Projector", "HT", "PS_MACROS").executeActions();
               }
               catch(e) {
                  com.randman.utils.deleteWaitPopup();
                  com.randman.utils.trace(e, 0);
                  message = "2. Could not set preamp output to\nprojector. Try again.";
                  com.randman.utils.createPopup(message);
                  return 0;
               }
            }
         }

         // If JVC projector is still powering up, getHTProjectorState()
         // returns "". In addition, shortly after
         // "Cooling down", the projector might return "" before it starts returning Off.
         if (HTProjectorState == "") {
            com.randman.utils.deleteWaitPopup();
            message = "Cannot determine projector state.\nIt may be powering on.\nTry again.";
            com.randman.utils.createPopup(message);
            return 0;
         }

         if (HTProjectorState == "Cooling down") {
            com.randman.utils.deleteWaitPopup();
            message = "Projector cooling down.\nPlease wait for the projector's\nfan to turn off before trying to\nuse the home theater again.";
            com.randman.utils.createPopup(message);
            return 0;
         }

         if (HTProjectorState == "Emergency") {
            com.randman.utils.deleteWaitPopup();
            message = "Projector has state Emergency.\nPlease diagnose the issue.";
            com.randman.utils.createPopup(message);
            return 0;
         }

         if (HTProjectorState != "On") {
            rc = 1;
            try {
               // HT System On reusable macro turns on the projector via an RS-232 command:
               com.randman.utils.trace("Turning on projector.", 1);
               CF.widget("System On", "HT", "PS_MACROS").executeActions();
            }
            catch(e) {
               com.randman.utils.deleteWaitPopup();
               com.randman.utils.trace(e, 0);
               message = "Could not turn on projector.\nTry again.";
               com.randman.utils.createPopup(message);
               return 0;
            }
            // Turn off all non-ambiance lights:
            com.randman.homeseer.runEvent("Pronto", "Pronto - Home Theater Non-Ambiance Lights - Off");

            // Turn on ambiance lights:
            com.randman.homeseer.controlDeviceByLabel(992,"On"); // 992: Step Lights
            com.randman.homeseer.controlDeviceByLabel(989,"On"); // 989: Lightstrips

            minPcnt = maxPcnt + 1;
            maxPcnt = maxPcnt + 100 * waitAmbianceLights/waitTotal;
            com.randman.utils.updateWaitPopup(waitAmbianceLights, minPcnt, maxPcnt, "Powering projector.");

            // Turn off ambiance lights and turn on speaker lights (for "IMAX" speaker effect):
            com.randman.homeseer.controlDeviceByLabel(989,"Off"); // 989: Lighstrips
            com.randman.homeseer.controlDeviceByLabel(1007,"Off"); // 1007: Sconces
            com.randman.homeseer.controlDeviceByLabel(991,"On");  // 991: Speaker lights

            minPcnt = maxPcnt + 1;
            maxPcnt = maxPcnt + 100 * waitSpeakerLights/waitTotal;
            com.randman.utils.updateWaitPopup(waitSpeakerLights, minPcnt, maxPcnt, "Powering projector..");
            com.randman.homeseer.controlDeviceByLabel(991,"Off");  // 991: Speaker lights

            // Since the projector was turned on before the logic to control
            // the lights, reduce the projector on wait time by the amount of time
            // the "light show" was being done:
            waitProjectorOnRemaining = waitProjectorOn - waitAmbianceLights - waitSpeakerLights;

            minPcnt = maxPcnt + 1;
            maxPcnt = maxPcnt + 100 * waitProjectorOnRemaining/waitTotal;
            com.randman.utils.updateWaitPopup(waitProjectorOnRemaining, minPcnt, maxPcnt, "Powering projector...");

            com.randman.homeseer.controlDeviceByLabel(992, "Off"); // 992: Step Lights

            if (com.randman.jvc_projector.switchProjectorInput("HDMI 1") == false) {
               com.randman.utils.deleteWaitPopup();
               message = "Error. Could not change\nprojector input to HDMI1.\nRetry.";
               com.randman.utils.createPopup(message);
               return 0;
            }
         } // if (HTProjectorState != "On")
         com.randman.utils.deleteWaitPopup();
         return rc;
   } // HTSystemOn()
OP | Post 4 made on Friday June 10, 2022 at 10:45
randman
Long Time Member
Joined:
Posts:
June 2003
416
Below is a screenshot of my pronto showing the please wait dialog box.
It's while waiting for the projector to finish starting (about a minute) when I'd like to do a light show.


Post 5 made on Friday June 10, 2022 at 20:42
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,992
This will definitely need to be reworked.

See the dev guide for examples on timed countdowns or timers using Activity.scheduleAfter. Also search this forum for posts from Barry and others about task queues.

I'm busy with work tonight but maybe could assist/chat in the next week.
Lyndel McGee
Philips Pronto Addict/Beta Tester
Post 6 made on Friday June 10, 2022 at 23:49
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,992
Several questions - You said you were using RFX9600 for serial. In your startup script is it only 1-way serial or do you need 2 way?

I see what you are trying to do. You are needing a way to convert asynchronous activities into a set of sequential tasks that will drive your UI.

To do something like this, you can use concepts such as task queues or finite state machines. You break out major items (turn on projector, set preamp input, etc) into tasks (functions) that can be called once the previous one completes.

The "state" is a variable that keeps track of all the permutations possible in your code. For example, once preamp input is set, the state variable changes from awaiting preamp input set -> preamp input set -> do next task.

So you then use scheduleAfter and an array of objects that serve as your task queue.


Next question - Do you have any raspberry pi's in your setup?

The reason I ask the 2nd question as I have nodejs code that emulates an RFX9600 but does not do IR. It runs as a service on a pi within my home. The emulator has software serial ports and software relays/inputs.

The serial ports will echo (loopback) whatever is sent to them and the relay state is passed along to the corresponding input. I use this capabilty in my ActionLists by sending 1-way serial actions to the Pi RFX9600. Then, on my progress page, I'm constantly polling (reading) the response from the serial port that received message during execution of the action list.

Your setup is not as simple as mine in that you need to send HTTP stuff to homeseer. Either way, I'm sure we can find a happy medium but you will need to get past "procedural" models of coding and move into asynchronous models of coding.

If you are in the US or have Facetime/whatsapp, I'd be happy to discuss maybe on Monday/Tuesday of next week.
Lyndel McGee
Philips Pronto Addict/Beta Tester
Post 7 made on Saturday June 11, 2022 at 08:24
buzz
Super Member
Joined:
Posts:
May 2003
4,366
Task queue is the way to go. I hate PW (Please Wait). I can't use Pronto for new jobs, but I had a task queue that I would pass from Activity to Activity. After you've developed the capability it will fundamentally change your whole approach. Rather than 'Projector Power', PW, 'Projector Input', 'Receiver Power', PW, 'Receiver Input', "Blu-Ray Power", PW, .... The sequence will become 'Projector Power', 'Projector Input (+60.00)', 'Blue-ray Power, 'Receiver Power', 'Receiver Input (+1.00)', 'Start light show', 'End light show (+60.00)' ...

While it's hard to bury a 60 second wait, for more reasonable devices I could start the slowest device (usually the TV), follow through with setting up the other devices and dump the user at picking the cable channel. By the time that the user is done with picking the cable channel, most of the other devices are ready to go and there is no need for a PW. The whole system seems more responsive to the user.

I also keep track of a unit's power status (when practical) because there is no point in waiting 60 seconds for a projector if it is already warmed up.

This is also handy for cable boxes that respond slowly. I use icon based cable channel selection and the digits must be metered out at a pace that the box can execute. I'm also very fast when I manually enter channel numbers. Since the box cannot execute at that pace, there will be missing digits and frustration. I queue the digits as entered and meter them out to the box at a more reasonable pace. Keep the inter-digit wait time symbolic. This will allow you to optimize the digit spacing by editing a single constant. (cable boxes can change or be updated)

I used an RFX9400 or RFX9600 for IR commands in order to avoid issues created by concealed devices or the user pointing the Pronto in the wrong direction. (It's unreasonable to expect the user to keep the Pronto pointed in the right direction for the full 60 seconds)
OP | Post 8 made on Saturday June 11, 2022 at 16:33
randman
Long Time Member
Joined:
Posts:
June 2003
416
Thanks, Lyndel & buzz for your input.

>> Several questions - You said you were using RFX9600 for serial.
>> In your startup script is it only 1-way serial or do you need 2 way?

I need 2 way. My processor and projector are connected via RS232 to my RFX9600 so I can check their state.

>> buzz: I also keep track of a unit's power status (when practical)
>> because there is no point in waiting 60 seconds for a projector
>> if it is already warmed up.

Same here. If the projector is already on, I don't do any waits.
The RS232 queries allows me to use the same function regardless
of whether they are powered on or not. The timings of how long the processor and projector take to start don't change (unless I buy new equipment), so instead of re-polling their power status after initially querying their status and starting them, I've already accounted for how long they take to start,
and have coded the script with appropriate "please wait" times. Also, depending upon the device, just because its RS232 says it has already "powered on", doesn't necessarily mean it's ready to accept new commands (such as switching inputs). Hence, an additional delay after the device says it has powered on state may still be needed.

I really only need the "please wait" displayed in the scenario where the projector
needs to be powered on, given how long it takes. I like to have a friendly "please wait" dialog box for users so they can see the percent complete, so they know there is progress and they don't think the Pronto has hung.

>> Do you have any raspberry pi's in your setup?

Yes. (Aside: I have a pi connected to an APC UPS via USB. The pi runs apcupsd, which the Pronto queries via getHTTP so it can display UPS data). I use the RFX9600 for both IR and RS232. Using a Pi for my Pronto seems
interesting, but may be more than what I want to tackle at this time.


>> ...use scheduleAfter and an array of objects that
>> serve as your task queue.

I tried searching for "task queue" in this forum, but I didn't see particularly relevant hits. I'll need to look more.

Anyway, based on my testing, it looks like getHTTP() calls are not processed at the time they are called. They are processed after the calling function/script has finished/returned. I decided to simplify things. Rather than calling multiple getHTTP() calls inside my HTSystemOn() function, I consolidated all the
light control commands into one HomeSeer event (automation task). My plan was to have one HomeSeer event do all the turning on and turning off of all my lights with the necessary delays in between. It would be HomeSeer's problem rather than Pronto's. All Pronto needs to do is call one HomeSeer event and the HomeSeer event will handle the light show while Pronto worries about turning on components.

Below was my Button script in Pronto:

// Start processor, projector, switch inputs, etc:
if (HTWatch("Shield") == true) {
this.scheduleActions(); // <= Jump to Nvidia Shield device's activity
}

Previously, HTWatch() called the HomeSeer events to turn on/off the lights, while turning on the processor, projector, displaying "Please Wait", etc. I changed HTWatch() to remove all the getHTTP() calls that send commands to HomeSeer, so I changed the above Button script to:

// use runEvent() function which uses 1 getHTTP() call to invoke a
// HomeSeer event to do the light show:
runEvent("Pronto", "Pronto - Lights - HT Startup");
if (HTWatch("Shield") == true) {
this.scheduleActions(); // Jump to Nvidia Shield device's activity
}

From my testing, the getHTTP() call that runEvent() invokes does NOT run until after HTWatch() finishes. So, the HomeSeer event isn't called until AFTER a minute after runEvent() is called, and the projector has fully started.

I guess this is just how the Pronto engine (or at least getHTTP) works? Seems that Pronto just puts any getHTTP calls in a queue and invokes them only after the calling script has finished?

Any easy solution to this? Using executeActions(), scheduleActions() or scheduleAfter() in order to run the runEvent() function BEFORE HTWatch() doesn't help. Or maybe I should use scheduleAfter() to invoke HTWatch() instead, so that runEvents()'s getHTTP() is called BEFORE HTWatch() is processed?

I have a couple of other options if I don't have luck with this...:

1. I have a relay that flips when my processor turns on. A sensor is connected to the relay, which HomeSeer monitors. If HomeSeer senses that they relay has flipped, I can have HomeSeer run the HomeSeer event to do the light show... Only disadvantage is if I'm just doing maintenance (turning things on and off manually), I may not want a light show.

2. Upgrade my bulb-based projector to a new laser projector... The new laser projectors start much faster... Oh, that's too expensive a solution for this problem :-).

EDIT: fixed formatting of code.
Post 9 made on Saturday June 11, 2022 at 17:16
Lyndel McGee
RC Moderator
Joined:
Posts:
August 2001
12,992
You will never get what you want until you refactor/rework what you have.

Have a look at the Philips HTTP Library code. It uses Asynchronous TCP sockets. Looks complex but is all driven on make a request and wait for data.

For your Option 1 above, configure 2 events, one that does light show and one that does not.

If the pronto is invoking them then invoke one vs the other. If homeseer is detecting something, then that's another issue.

The notion of a please-wait page is cool but there are other ways you could accomplish what you are looking to do. It would involve the use of Page Scripts and jumping to multiple or the same page and setting globals (System.setGlobal) or activity-based variables to control the progress state.

My offer still stands about facetime/whatsapp/phone call. Email me details and your config if you like to my email addy on profile (you likely already have it).

Lyndel
Lyndel McGee
Philips Pronto Addict/Beta Tester
Post 10 made on Saturday June 11, 2022 at 23:33
buzz
Super Member
Joined:
Posts:
May 2003
4,366
On June 11, 2022 at 16:33, randman said...
The timings of how long the processor and projector take to start don't change (unless I buy new equipment), so instead of re-polling their power status after initially querying their status and starting them, I've already accounted for how long they take to start, and have coded the script with appropriate "please wait" times. Also, depending upon the device, just because its RS232 says it has already "powered on", doesn't necessarily mean it's ready to accept new commands (such as switching inputs). Hence, an additional delay after the device says it has powered on state may still be needed.

As Lyndel suggested, don't use any blocking delays.

With a task queue you push any needed commands into the queue with a "due time" ... and you probably don't need to worry about them again as the queue works through the list. If your task queue is clever enough, you can find and/or cancel commands or verify that a command has executed. If a critical, scheduled command has not yet executed and the user must wait, then you can start up a PW, but only for the remaining critical time. Make sure that your PW is not blocking the task queue. All sorts of clever, attention diverting activities can be devised to ease the pain of that projector start.

For example, if you must wait for the projector and the receiver starts quickly, you can allow the user to go to the receiver control page and work with selecting surround modes or similar. Then, if you have a multi-disc player, you can go to its control page and select a disc to play -- after searching through a library of titles if you have these stored on the Pronto. If the disc player is slow starting up, queue its commands too.

An interesting little "glitch" can occur if you use blocking delays. Suppose that the receiver starts up, playing loudly, and the user is trapped in a blocking PW with no option to lower the Volume. This can create tense situations in the household. With a task queue the Volume control can always be active.
OP | Post 11 made on Sunday June 12, 2022 at 16:52
randman
Long Time Member
Joined:
Posts:
June 2003
416
Lyndel & Buzz - again thanks for your input! Lyndel, many thanks for your offer to discuss this further offline. Unfortunately, I won't have much time to work on this in the near term.

You're suggestions for a task queue looks good, but probably more than I have time to tackle with at the moment.

Buzz - funny that you mentioned the scenario of the receiver starting up too loudly while a PW is blocking... It actually happened to me (and other folks), so I eventually changed my processor to use the same pre-defined volume level whenever it turns on :-).

Once I get more free time, I'll revisit having to use PW and task queues. I normally don't use PW except for the scenario of the projector taking long to start.

Anyway, for now, I have an alternate method of doing the light show. I just connected one of my processor's 12 volt triggers to my Panamax surge protector, whose switched outlets turn on and off based on the 12-volt trigger. I have a relay connected to one of the Panamax' switched outlets, so it detects when it has power or not. I hard-wired a Z-Wave door/window sensor to the relay. The Z-Wave sensor sends notification to HomeSeer whenever the processor is turned on or off, so I can kick off HomeSeer events in response. For example, when the processor is turned on, HomeSeer turns on the closet exhaust fan to keep the closet cool. I wrote a HomeSeer event ("Pronto - Lights - HT Startup") to do the light show, and can be called by HomeSeer when the processor is turned on. Doing the light show in HomeSeer frees up (and simplifies) the Pronto code, and I can easily change the light show without having to re-download the Pronto configuration to my Pronto.




Jump to


Protected Feature Before you can reply to a message...
You must first register for a Remote Central user account - it's fast and free! Or, if you already have an account, please login now.

Please read the following: Unsolicited commercial advertisements are absolutely not permitted on this forum. Other private buy & sell messages should be posted to our Marketplace. For information on how to advertise your service or product click here. Remote Central reserves the right to remove or modify any post that is deemed inappropriate.

Hosting Services by ipHouse