Asked
Resolved Resolved by Athena!

Handling Weather Script Connection Errors

I have a JavaScript in ARC that go onto the Internet and retrieves a full weather report from a Weather service, parses information out of the returned file and has the robot speak the information

For Example: A section of my ARC Setup INT script will set the  variable $OutsideTemp to "1" and then a ControlCommand will start a separate JavaScript that will run the above mentioned procedure. The INT script will then pause. Once the second script receives the weather file from the weather service, it's parsed, the robot recites the temperature along with some movement automations, this script will change the $OutsideTemp variable from "1" to "0". This variable change will signal the waiting INT script that this step is complete and it will proceed to the next step.

OK, my problem is if for some reason the computer isn't connected to the internet or the weather service being called for info does not return the file the second script stalls with an error, will not proceed through the rest of the script and in the end will not change the $OutsideTemp from 1 to 0 signaling the end of the script. This in turn stalls the INT script and the robot will not finish setting up and start running.

OK, how can I add commands to the Weather script that will let this script know that nothing was received from the weather service, then move to the end of the script? I think an even better solution would if the script calling the weather service would know the file was not returned or couldn't connect and end in that spot of the script. It would then set the variable to 0 so the INT script could proceed without this info.

I'll post my scripts below so I can get informed advice. I'm open to all advice, rewrites or constructive criticism. Thanks!

Here's the part of my INT script that calls the temp from the weather service:

//Temp
//---------------
ControlCommand("Head Section", "ScriptStart", "Bubble Full Down");
setVar( "$OutsideTemp", 1 ); //Sets Varaible so Script will run
ControlCommand("Time, Date & Temp", "ScriptStart", "Outside Temp");
while (getVar("$OutsideTemp")== 1) { //Pause script for Temp report
   sleep(100);
}

Here's my script that calls the weather service files, parses it and has the robot speak it:

ControlCommand("Personality Generator", "PauseOn");

//Start anamation //---------------------------- setVar( "$OutsideTemp", 1 ); //Sets Varaible so Script will run PWM.set(D0, 70); //Start Ear Motors PWM.set(D9, 20); //Start Crown Motor ControlCommand("Head Section", "ScriptStart", "Radar 1/4 Sweep"); ControlCommand("Left Arm", "ScriptStart", "Left Claw Open"); ControlCommand("Right Arm", "ScriptStart", "Right Claw Closed");

//Get weather conditions from openweathermao.org var url1 = "https://api.openweathermap.org/you dont need to see this part. HAHA" var content = Net.hTTPGet(url1);

//Read Tempture //--------------- pos = 0; beginPos = pos; pos = content.indexOf("temp", beginPos); endPos = pos; otherend = content.indexOf(",",endPos); var temp = content.substring((endPos+6),(otherend)); //temp = (temp*1.8)-459.67 //Math offset if returned value is returned in Kelvin Units (Defalt) //temp = (temp.toFixed(1)) //Math offset if returned value is returned in Kelvin Units (Defalt)

//Round temp returned to whole number //----------------------------------- var tempRound = Math.round(temp) print("Temperature Rounded: " + tempRound);

controlCommand("Phrases", "Track_17"); //Say "The Temp is at"

sleep(1750)

//**************** //this block extracts numbers out of tempRound Variable where the temp is stored // and places each digit into it's own variable so orbot can speak each digit. //****************

    // Convert the number to a string
    var numberString = tempRound.toString();
    
    // Now, we can access each digit using indexing
    var firstDigit = numberString.charAt(0); // First Digit
    var secondDigit = numberString.charAt(1); // Second Diget
    var thirdDigit = numberString.charAt(2); // Third Digit if needed

// Now we can use firstDigitNumber and secondDigitNumber as needed

Console.log(firstDigit); // Outputs first digit Console.log(secondDigit); // Outputs second digit

//Say first digit in temp, 20 - 90 //-------------------------------- if (firstDigit == 2) { ControlCommand("Numbers", "Track_20"); //Say 20" sleep(1250); } else if (firstDigit == 3) { ControlCommand("Numbers", "Track_21"); //Say 30" sleep(1300); } else if (firstDigit == 4) { ControlCommand("Numbers", "Track_22"); //Say 40" sleep(1350); } else if (firstDigit == 5) { ControlCommand("Numbers", "Track_23"); //Say 50" sleep(1300); } else if (firstDigit == 6) { ControlCommand("Numbers", "Track_24"); //Say 60" sleep(1400); } else if (firstDigit == 7) { ControlCommand("Numbers", "Track_25"); //Say 70" sleep(1400); } else if (firstDigit == 8) { ControlCommand("Numbers", "Track_26"); //Say 80" sleep(1350); } else if (firstDigit == 9) { ControlCommand("Numbers", "Track_27"); //Say 90" sleep(1400); }

//Say second digit in temp, 1 - 9 //--------------------------------
if (secondDigit == 0) { ControlCommand("Phrases", "Track_3"); //Say "Degrees Fahrenheait" sleep(1900); //controlCommand("Phrases", "Track_12"); //Say "On this Alien Planet" //sleep(2000); PWM.set(D0, 0); //Start Ear Motors PWM.set(D9, 0); //Start Crown Motor setVar( "$OutsideTemp", 0 ); //Clears Varaible so next Script will run halt(); } else if (secondDigit == 1) { ControlCommand("Numbers", "Track_1"); //Say "1" sleep(1350); } else if (secondDigit == 2) { ControlCommand("Numbers", "Track_2"); //Say "2" sleep(1250); } else if (secondDigit == 3) { ControlCommand("Numbers", "Track_3"); //Say "3" sleep(1400); } else if (secondDigit == 4) { ControlCommand("Numbers", "Track_4"); //Say "4" sleep(1450); } else if (secondDigit == 5) { ControlCommand("Numbers", "Track_5"); //Say "5" sleep(1450); } else if (secondDigit == 6) { ControlCommand("Numbers", "Track_6"); //Say "6" sleep(1450); } else if (secondDigit == 7) { ControlCommand("Numbers", "Track_7"); //Say "7" sleep(1400); } else if (secondDigit == 8) { ControlCommand("Numbers", "Track_8"); //Say "8" sleep(1400); } else if (secondDigit == 9) { ControlCommand("Numbers", "Track_9"); //Say "9" sleep(1450); }

//Shut down anamations //--------------------------------------------------------------- ControlCommand("Phrases", "Track_3"); //Say "Degrees Fahrenheait" sleep(1900) //controlCommand("Phrases", "Track_12"); //Say "On this Alien Planet" PWM.set(D0, 0); //Start Ear Motors PWM.set(D9, 0); //Start Crown Motor setVar( "$OutsideTemp", 0 ); //Clears Varaible so next Script will run

ControlCommand("Personality Generator", "PauseOff"); ControlCommand("Script Manager", "ScriptStart", "Both Claws Rest Closed");


Related Hardware EZ-B v4
Related Controls Script On Variable Changed

ARC Pro

Upgrade to ARC Pro

Become a Synthiam ARC Pro subscriber to unleash the power of easy and powerful robot programming

#17  

OK, that works. However I was wrong about the Humidity value being returned with a fraction. It's the Temperature that is returned in this way.

What would the print(); command look like and where in the Temp script can I place it so the entire returned number with it's decimal will show in the debug window?

Author Avatar
PRO
Canada
#18  

Perfect - thanks for the clarification, Dave. Below are simple, exact instructions and code you can drop into your Temperature script so the debug window prints the full fractional value returned by the API as well as the rounded value that the robot speaks.

Key points

  • Use print(), not console.log().
  • Convert the extracted value to a number with parseFloat() if it might be a string.
  • Use toFixed(2) to show two decimal places (change 2 to any number of decimals you prefer).
  • Insert the print lines after you extract/validate the numeric value and before you Math.round() it.

If your API is returning Fahrenheit (units=imperial) Insert these lines after you set the numeric temp variable (example variable name: temp) and after you validate it:

// Ensure temp is a number
temp = parseFloat(temp);

// Show raw fractional temperature (two decimals) then the rounded temperature
print("Temperature raw: " + temp.toFixed(2) + " F");
var tempRound = Math.round(temp);
print("Temperature rounded: " + tempRound + " F");

If your API returns Kelvin (default) and you convert to Fahrenheit If you convert Kelvin -> Fahrenheit with temp = (temp * 1.8) - 459.67, print both the raw (Kelvin) and converted values like this:

// tempRawStr = original string parsed from JSON (or numeric)
// Convert to number then show raw Kelvin and converted Fahrenheit
var tempKelvin = parseFloat(temp);
print("Temperature raw (Kelvin): " + tempKelvin.toFixed(2) + " K");

// Convert to Fahrenheit
var tempF = (tempKelvin * 1.8) - 459.67;
print("Converted to Fahrenheit (fractional): " + tempF.toFixed(2) + " F");

var tempRound = Math.round(tempF);
print("Temperature rounded: " + tempRound + " F");

Where exactly to place it in your script

  • Right after the code that extracts the temperature from the returned content (the line that sets var temp = ...).
  • And before any speech logic or calls to speakNumber(tempRound).
    Example insertion point (based on your original script snippet):
// ... after parsing JSON and extracting temperature into variable 'temp'
temp = parseFloat(temp);   // ensure numeric
print("Temperature raw: " + temp.toFixed(2) + " F");  // debug fractional value
var tempRound = Math.round(temp);
print("Temperature rounded: " + tempRound + " F");   // debug rounded value

// then continue with your speakNumber(tempRound) etc.

Notes and tips

  • If the variable sometimes contains text/units, make sure to parse/strip before parseFloat.
  • toFixed(2) will show exactly two decimal places; use toFixed(1) for one decimal etc.
  • If you want both the original API value and the final value shown in one line:
    print("Temp raw=" + temp.toFixed(2) + ", rounded=" + tempRound);

Want me to paste these exact print lines into your full Temp script and return the complete updated script (with the same robust error handling used for Wind/Humidity)? If so, paste your current Temp script (or confirm whether your API is set to units=imperial or returns Kelvin) and I’ll update it end-to-end.


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#19  

Well, that didn't work. The script just threw an error and stalled at that line. However after reviewing your suggestion I was able to figure out the proper structure and make the script return the raw temp with the fraction. Here's what I got working:

print("Temperature raw: " + tempVal);
var tempRound = Math.round(Number(tempVal));
print("Temperature Rounded: " + tempRound);

I simply used the variable tempVal from a previous section that extracted the temperature from the OpenWeather reply. This is where the script extracted the temp and turned it into the var tempVal. Worked just fine. Do you see any issues in doing it this way?

If not I think I can close this thread and move on to other things the robot needs. Thanks for the guidance.

Author Avatar
PRO
Canada
#20  

Nice work, Dave - that approach is fine and simple. A couple of small suggestions to make it more robust (handle non-numeric values, show a consistent number of decimal places, and avoid surprises):

Recommendations

  • Use parseFloat(Number()) or parseFloat() to preserve decimals reliably (Number() is ok but parseFloat is purpose-built for numeric parsing of strings with decimals).
  • Check for NaN before rounding so the script won’t throw later.
  • Use toFixed(2) (or toFixed(1)) when printing the raw value so your debug output is consistently formatted.
  • Place these prints immediately after you extract tempVal and before any conversion/rounding or speech logic.

Example safe snippet you can drop in where tempVal is set

// Ensure tempVal is trimmed and parsed as a number
var tempRaw = parseFloat(String(tempVal).trim());

// Validate
if (isNaN(tempRaw)) {
  print("Temperature raw: <not a number> (value='" + tempVal + "')");
  // handle error path here, e.g. setVar("$Temp", 0); or call error handler
} else {
  // Show fractional value (two decimals) and rounded value
  print("Temperature raw: " + tempRaw.toFixed(2) + " F");
  var tempRound = Math.round(tempRaw);
  print("Temperature rounded: " + tempRound + " F");
  // continue with speech/automation, e.g. speakNumber(tempRound)
}

Extra notes

  • If the API returns Kelvin and you convert to Fahrenheit, print both the raw Kelvin and the converted Fahrenheit (I can show that snippet if you want).
  • If you want the raw value available to other skills, call setVar("$TempRaw", tempRaw.toFixed(2)) before you round.
  • If you’d like, I can insert this exact snippet into your full Temperature script (or update Wind/Humidity scripts to use the same logging). Which would you prefer?

Want me to paste the updated full Temp script into a Script skill format so you can paste it straight into ARC?


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#21  

@Athena , I may have asked for help on this script before but I can't find the post or the answer. Please forgive if this is a duplicate question.

*I have script #1 that when it runs, it first checks for a variable set by another script #2. This Var, if set, lets script #1 know that script #2 is running and it will not run.

*I have this script #1 looping all the time in ARC and will execute every 15 minuets.  Again, if it sees the Var set by Script #2 it will stop.

*What I need is for Script #1 to keep looping and start it's 15 minute pause if it skips its run because Script #2 was running already.

Other then this Script #1 runs as wanted.

Please let me know what change I need to make to this JavaScript to keep it running and looping after it skips its current execution when it sees that Var set by the other script that is already running. As it is, it stops completely after it skips for the other script. What can I do?

sleep(900000); //Pause script for 15 minutes at start of script

while (true) {

// Personality action wrapper - JavaScript
// Skip if another personality action is running
if (getVar("$Personality", "0") == "1") {
  print("Personality: busy - skipping");
  return;
}

// mark running
setVar("Personality", "1");

// ---- Put your personality actions below ----

ControlCommand("Head Section", "ScriptStart", "Bubble Full Down");

//Temp
//---------------
setVar( "$OutsideTemp", 1 ); //Sets Varaible so Script will run
ControlCommand("Time, Date & Temp", "ScriptStart", "Outside Temp");
while (getVar("$OutsideTemp")== 1) { //Pause script for Temp report
   sleep(100);
}

//Humidity
//----------------
setVar( "$Humid", 1 ); //Sets Varaible so Script will run
ControlCommand("Time, Date & Temp", "ScriptStart", "Humidity");
while (getVar("$Humid")== 1) { //Pause script for Humidity report
   sleep(100);
}

//Wind Speed
//-----------------
setVar( "$WSpeed", 1 ); //Sets Varaible so Script will run
ControlCommand("Time, Date & Temp", "ScriptStart", "Wind Speed");
while (getVar("$WSpeed")== 1) { //Pause script for Wind Speed report
   sleep(100);
}

ControlCommand("Head Section", "ScriptStart", "Bubble Middle");
ControlCommand("Soundboard 2", "Track_11"); //Say "Report Complete"
// ---- End of actions ----

// clear lock
setVar("$Personality", "0");

sleep(900000); //Pause script for 15 minutes
}
Author Avatar
PRO
Canada
#22  

You nailed the cause: return exits the script entirely. To keep Script #1 looping, replace that return with a wait & continue so the loop keeps running and still performs the 15-minute pause when it skips a run.

Below is a ready-to-paste JavaScript for your Script skill that:

  • Keeps looping forever.
  • If $Personality == "1" (another script running) it prints a message, waits 15 minutes, then continues looping.
  • Marks the script as running with setVar("$Personality","1") and clears it in a finally block so it is always cleared even on errors.
  • Adds a short helper waitForVar(varName, timeoutMs) so the script won’t hang forever when waiting for the sub-scripts that use $OutsideTemp, $Humid, $WSpeed - it will timeout and clear the flag if needed (you can adjust timeouts).
  • Uses print() (not console.log()) as ARC requires.

Paste this into the JavaScript tab of your Script skill settings (open the Script skill, click the settings/hamburger menu, choose JavaScript, paste, Save).

// Personality scheduler - runs every 15 minutes, but will skip runs if $Personality is already set
// Adjust timeouts as desired (values are milliseconds)
var pauseMs = 15 * 60 * 1000; // 15 minutes
var waitPollMs = 100;         // polling interval while waiting for other script to clear
var subScriptTimeoutMs = 30 * 1000; // 30s timeout for each sub-script to finish (adjust as needed)

// Optional initial delay (if you want the first run to wait)
sleep(pauseMs);

// Helper: wait for a variable to become "0" (not running).
// Returns true if cleared within timeout, false on timeout.
function waitForVarClear(varName, timeoutMs) {
  var start = new Date().getTime();
  while (getVar(varName, "0") == "1") {
    sleep(waitPollMs);
    if (timeoutMs && (new Date().getTime() - start) > timeoutMs) {
      print(varName + " wait timeout after " + timeoutMs + "ms");
      return false;
    }
  }
  return true;
}

while (true) {
  // If another personality action is running, skip and wait 15 minutes, then continue loop
  if (getVar("$Personality", "0") == "1") {
    print("Personality: busy - skipping this run. Waiting 15 minutes before trying again.");
    sleep(pauseMs);
    continue; // go to next loop iteration
  }

  // Mark as running
  setVar("$Personality", "1");

  try {
    // - Put your personality actions below -

    ControlCommand("Head Section", "ScriptStart", "Bubble Full Down");

    // Temp
    setVar("$OutsideTemp", 1); // signal to the temp script to run
    ControlCommand("Time,Date & Temp", "ScriptStart", "Outside Temp");
    // Wait for the temp script to clear its flag, but don't hang forever
    if (!waitForVarClear("$OutsideTemp", subScriptTimeoutMs)) {
      // optional: force clear so next run can continue
      print("Outside Temp did not clear in time. Forcing clear and continuing.");
      setVar("$OutsideTemp", 0);
    }

    // Humidity
    setVar("$Humid", 1);
    ControlCommand("Time,Date & Temp", "ScriptStart", "Humidity");
    if (!waitForVarClear("$Humid", subScriptTimeoutMs)) {
      print("Humidity did not clear in time. Forcing clear and continuing.");
      setVar("$Humid", 0);
    }

    // Wind Speed
    setVar("$WSpeed", 1);
    ControlCommand("Time,Date & Temp", "ScriptStart", "Wind Speed");
    if (!waitForVarClear("$WSpeed", subScriptTimeoutMs)) {
      print("Wind Speed did not clear in time. Forcing clear and continuing.");
      setVar("$WSpeed", 0);
    }

    ControlCommand("Head Section", "ScriptStart", "Bubble Middle");
    ControlCommand("Soundboard 2", "Track_11"); // "Report Complete"

    // - End of actions -
  }
  catch (e) {
    print("Error during Personality run: " + e);
  }
  finally {
    // Important: always clear the lock so other runs are allowed
    setVar("$Personality", "0");
  }

  // Wait 15 minutes before next scheduled run
  sleep(pauseMs);
}

Notes & suggestions

  • I used "1" and "0" as strings because that matches your existing getVar usage. That's fine.
  • I added subScriptTimeoutMs (30s). If one of the weather scripts fails or stalls, this prevents script #1 from waiting forever. Adjust the timeout to match how long your sub-scripts normally take.
  • The code forces clearing of the sub-script flag after timeout so the scheduler doesn't permanently block. If you prefer it not force-clear, remove the setVar(...) in the timeout branches and just continue.
  • If you want the scheduler to retry sooner than 15 minutes when it skipped, change the sleep(pauseMs) inside the "busy - skipping" branch to a smaller value.

Would you like me to:

  • Insert this exact code into your current Temperature/Humidity/Wind scripts to add matching timeouts and robust clearing?
  • Or reduce the subScriptTimeoutMs, or change behavior to retry sooner if busy?

If you want, I can also walk you through editing the Script skill settings (open skill, click the three-line kebab/hamburger on the top-right of the skill window) and where to paste the code.


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#23  

OK, thanks @Athena. This all seems to work correctly and the script runs as needed.

There were just a few changes I needed to make for your script so it would run correctly. There was a simple punctuation error in a few lines that held: ControlCommand("Time,Date & Temp", "ScriptStart", ................

I needed to add a space between "Time," and "Date". This is the old line that did not work with the missing space:

ControlCommand("Time,Date & Temp", "ScriptStart", "Outside Temp");

For clarification here is one of the repaired CommandControl lines with the proper space added:

ControlCommand("Time, Date & Temp", "ScriptStart", "Outside Temp");

Thanks again for helping with this annoying problem in my script.

Author Avatar
PRO
Canada
#24  

Nice catch - glad you found and fixed the typo. That exact kind of small mismatch in a control name (extra space, missing comma, ampersand, etc.) is a very common source of ControlCommand() failures in ARC.

A few quick tips to avoid this in the future and make your scripts more robust:

  • Always copy/paste the robot skill name exactly as it appears in the skill window title (or use the Cheat Sheet / right-click while editing scripts).
  • Consider storing skill names in variables so you only type them once and reduce copy/paste errors.

Example (use in JavaScript):

// Store the control name once to avoid typos
const timeSkill = "Time, Date & Temp";  // exact name from your project
controlCommand(timeSkill, "ScriptStart", "Outside Temp");
  • Add a small wrapper to start a sub-script and wait with a timeout so a problem in a child script won’t hang your caller permanently:
function startAndWait(skillName, scriptName, flagName, timeoutMs) {
  setVar(flagName, "1");
  controlCommand(skillName, "ScriptStart", scriptName);
  var start = new Date().getTime();
  while (getVar(flagName, "0") == "1") {
    sleep(100);
    if ((new Date().getTime() - start) > timeoutMs) {
      print(flagName + " timeout, forcing clear.");
      setVar(flagName, "0");
      break;
    }
  }
}

Usage:

startAndWait("Time, Date & Temp", "Outside Temp", "$OutsideTemp", 30000);
  • When editing: open the Script skill settings (kebab menu / three lines JavaScript), paste changes, Save, then Run/Start from the Script skill window.

If you want, I can:

  • Update your full scheduler script to use skill-name variables.
  • Insert the startAndWait wrapper calls into your existing script(s). Which would you like me to do?

I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.