Remotely identify GF status

A long time ago, there was a promise that GF would release open source firmware: We’ll release a GPL-licensed firmware for Glowforge Did anything ever happen with that?

I’m not trying to write my own firmware. I’m hoping there is some kind of API for checking the GF status. Or if I can’t query it directly, then may through something like https://api.glowforge.com/gfcore/users/machines ? (When you’re logged in, this URL returns a json with the device name, state (unavailable, online, etc.), and the last time the bed camera was used.)

Basically, I want to know:

  1. Is my GF currently on or off?
  2. If it’s on, is it working on something or sitting idle?
  3. (bonus points) If it’s working on something, what’s it working on? (Project name, start time, expected end time.)
  4. (more bonus points) Get the last bed picture.

I have a few people who use the GF. I’d like to keep better track of how often it’s used (to schedule maintenance and cleaning). Also, if someone else is using it for the next hour, then maybe I won’t walk down the hallway to see if it’s in use.

Update: Script solution posted below: Remotely identify GF status - #8 by dr.krawetz

4 Likes

There is no public API, no. You might consider writing a browser extension to get the information from the app, which shows the status of all your machines at the top right.

5 Likes

What am I missing here? I can get any of that simply by logging in from anywhere.
My aura. Is offline. My Pro is online. Not active and there’s nothing on the bed.Oh, and I’m at work right now.

6 Likes

No.

6 Likes

From the web browser, it’s easy to get that information.
I want to get it as part of a script, like using curl or wget.

5 Likes

As you noted, there is an undocumented API. It has been fairly stable for many years, though of course that’s no guarantee that it won’t break as soon as you start relying on it.

11 Likes

Fair enough.
In that case, as @chris1 said, you can poke around in the undocumented API.

Glowforge won’t help you on that but they won’t try and stop you either. Thanx to little tweaks by him I get a bell every time a print is done processing and the chat is permanently gone.

9 Likes

Okay, nobody has a solution, so I built a solution. This is a PHP script. (Shuddup – I like php!) It emulates the login to the GlowForge cloud web interface and then retrieves the machine information. If it works, you’ll see a bunch of json output.

You only need to set your login email address, login password, and the path for storing the cookie file.

Warning: Since this script requires your username and password for getting into your glowforge account (and this forum), be very careful to not put it on a public-facing web server!

<?php
/**********************************************
 Script to check GlowForge status
 Created by Neal Krawetz, 2023-12-28
 License: Public domain, not responsible for how you use it or any damage it may cause.
 Use at your own risk!
 **********************************************/

global $CookieJar;

// BEGIN CUSTOMIZATION

$USER='YOUR_EMAIL_ADDRESS'; // this is your GF login
$PASS='YOUR_PASSWORD'; // this is your GF password
$CookieJar='/dev/shm/gf-cookies.txt'; // path to cookie storage
$Verbose=false; // enable for debugging

// END CUSTOMIZATION

function GetURL($method,$url,$parameters=false,$verbose=false)
  {
  global $CookieJar;
  $uas = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0';
  $options = array(
        CURLOPT_URL            => $url,     // set the URL
        CURLOPT_USERAGENT      => $uas,     // set the user-agent string
        CURLOPT_RETURNTRANSFER => true,     // return web page
        CURLOPT_HEADER         => false,    // DON'T return headers in addition to content
        CURLINFO_HEADER_OUT    => true,     // Show me the posted request
        CURLOPT_FOLLOWLOCATION => true,     // follow redirects
        CURLOPT_ENCODING       => "",       // handle all encodings
        CURLOPT_AUTOREFERER    => true,     // set referer on redirect
        CURLOPT_CONNECTTIMEOUT => 120,      // timeout on connect
        CURLOPT_TIMEOUT        => 120,      // timeout on response
        CURLOPT_MAXREDIRS      => 10,       // stop after 10 redirects
        CURLOPT_SSL_VERIFYPEER => true,     // Validate SSL Certificates
        CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST  => strtoupper($method), // request method
        CURLOPT_COOKIESESSION  => true,     // store session cookies
        CURLOPT_COOKIEJAR      => $CookieJar, // where to store cookies
        CURLOPT_COOKIEFILE     => $CookieJar, // where to read cookies
        );

  $out=array();
  if ($parameters !== false)
    {
    $options[CURLOPT_HTTPHEADER] = array('Content-Type: application/x-www-form-urlencoded');
    $options[CURLOPT_POST] = true;
    $options[CURLOPT_POSTFIELDS] = http_build_query($parameters);
    $out['post_parameters'] = $options[CURLOPT_POSTFIELDS];
    }

  $ch =  curl_init(); // curl handle
  curl_setopt_array($ch, $options);
  $out['body'] = curl_exec($ch);
  $out['err'] = curl_errno($ch);
  $out['errmsg'] = curl_error($ch);
  $out['header'] = curl_getinfo($ch);
  curl_close($ch);
  if ($verbose) { print_r($out); }

  return($out);
  }

/******************************************* 
 Check if it needs to get cookies
 *******************************************/
$NeedCookies=true; // assume it needs cookies
if (is_file($CookieJar))
  {
  // Check if cookies exist and have not expired
  $fp=fopen($CookieJar,"r");
  if ($fp)
    {
    // There are 3 necessary cookies:
    // #HttpOnly_.glowforge.com
    // #HttpOnly_.accounts.glowforge.com
    // .glowforge.com
    $Now=time();
    $Cookie=array();
    while($line=fgets($fp))
      {
      $line=explode("\t",$line);
      if (!isset($line[6])) { continue; } // bad line
      if (!is_numeric($line[4])) { continue; } // bad line
      if (intval($line[4])+30 <= $Now) { continue; } // needs to be valid for at least 30 seconds
      $Cookie[$line[0]]=1; // looks valid, so store it
      }
    fclose($fp);

    // Are all of the essential cookies valid?
    if (isset($Cookie['#HttpOnly_.glowforge.com']) &&
        isset($Cookie['#HttpOnly_.accounts.glowforge.com']) &&
        isset($Cookie['.glowforge.com']))
        {
        $NeedCookies=false; // already got good cookies!
        }
    }
  }

/******************************************* 
 Get cookies as needed.
 *******************************************/
if ($NeedCookies)
  {
  // Get initial Google cookies.
  if ($Verbose) { echo "Get cookies!\n"; }
  if (is_file($CookieJar)) { unlink($CookieJar); } // toss any old cookies
  $out=GetURL("GET","https://app.glowforge.com/");
  if (!isset($out['header']['http_code']) || (intval($out['header']['http_code'])!==200))
    {
    echo "Error: Bad return code.\n";
    print_r($out);
    exit;
    }
  // Grab the authenticity token!
  // E.g.: <input type="hidden" name="authenticity_token" value="BlaBlaBla" />
  // This token is supposed to deter bots and scripts, uh, like this script.
  $p=strpos($out['body'],'name="authenticity_token"'); // find the field
  $Tok=substr($out['body'],$p);
  $p=strpos($Tok,'value="'); // find the value
  $Tok=substr($Tok,$p+7);
  $p=strpos($Tok,'"'); // find the end of the value
  $Tok=substr($Tok,0,$p);
  #echo "Tok=$Tok\n";

  // Login to get session cookies.
  if ($Verbose) { echo "Post login\n"; }
  $Post=array();
  $Post['authenticity_token'] = $Tok;
  $Post['user[email]'] = $USER;
  $Post['user[password]'] = $PASS;
  $Post['user[remember_me]'] = 0;
  $Post['commit'] = 'Sign In';
  $out=GetURL("POST","https://accounts.glowforge.com/users/sign_in",$Post);
  if (!isset($out['header']['http_code']) || (intval($out['header']['http_code'])!==200))
    {
    echo "Error: Bad login.\n";
    print_r($out);
    exit;
    }
  } // if $NeedCookies

/******************************************* 
 Get machines! (Requires valid login and session cookies.)
 *******************************************/
$out=GetURL("GET","https://api.glowforge.com/gfcore/users/machines");
if (!isset($out['header']['http_code']) || (intval($out['header']['http_code'])!==200))
  {
  echo "Error: Bad get machines.\n";
  print_r($out);
  exit;
  }
echo $out['body']; // output the json
?>

Since it requires PHP, you can run it from the command-line, like:

php ./gf-getmachine.php > machines.json
cat machines.json | json_pp | less

If it needs to get the login cookies, then this may take 2-3 seconds to complete.
If it already has valid login cookies (from a previous run), then it takes less than a second to complete.

I don’t know if there’s a login limit or how often you can call it. I was running it every few minutes while trying to build and debug it, and I didn’t get locked out.

I strongly suggest NOT trying to abuse it by calling it ever few seconds (or more often). Otherwise, GF might notice and disable this capability or start locking accounts for abusive behavior. (I know I would if I were them.)

15 Likes