Overview

groov EPIC and RIO firmware can be updated with the groov Manage REST API.

Updating firmware is an involved process that uses several REST API endpoints. The basic procedure is:

  1. Get ready by collecting all necessary information, including the IP address or hostname of the device, an API Key for the device, and the firmware file.
  2. Use the /manage/api/v1/maintenance/update/prepare endpoint to prepare the groov Manage server for a firmware update.
  3. Divide the firmware file into multiple chunks, each 25,000,000 bytes. Upload each chunk using the /manage/api/v1/maintenance/update/apply endpoint.
  4. (Optional) Use the /manage/api/v1/maintenance/update/status endpoint to follow the progress of the update.
  5. (Optional) After the unit restarts itself, use the /auth/access/user/commission/status endpoint to follow the remaining progress.

The following steps are illustrated using an example Python program to show how this all works together. Most programming languages could do the same procedures, but might require additional libraries.

The example Python program is also useful just by itself, if you want to update firmware from a shell script.

Step 1 - Get Ready

Three pieces of information are needed to update a device:

  1. Address of the device
  2. Name and path of the firmware file
  3. API Key of an Admin user

In the example project, the address and filename come in through command-line arguments. The API Key can be provided either through a command-line argument or an environment variable name “GROOV_API_KEY”. While it’s beyond the scope of this page, please note that it’s considered a best practice to inject secrets like an API key into a command shell’s environment.

Step 2 - Initialize the groov Manage Server

Before starting to upload the firmware file, a “prepare” command must be sent to groov Manage.

This is done with a POST to the /manage/api/v1/maintenance/update/prepare endpoint.

The POST’s data payload is a JSON object and has a very important property called preserveSettings.

If preserveSettings is set to true, the device’s settings will be preserved, including user accounts, network settings, security certificates, etc.

If preserveSettings is set to false, nothing will be preserved and the device will need to be recommissioned.

For example:

{
  "preserveSettings": true
}

And like nearly all requests to groov devices, an HTTP Header for “apiKey” is required.

In the example project, all of this is done in the initialize_update() function.

# Ititialize the firmware update.
def initialize_update(address, api_key):

    # Tell the device that we are going to upload firmware.
    # POST to the "/manage/api/v1/maintenance/update/prepare" endpoint.
    # The data object has a very important property called "preserveSettings".
    # If set to True, the device's settings will be preserved.
    # If set to False, nothing will be preserved and the device will need to
    # be recommissioned.
    prepare_response = requests.post(
        "https://" + address + "/manage/api/v1/maintenance/update/prepare",
        json={
            "preserveSettings": True,

            # In 4.0 and ealier firmware, "backupNetwork" meant to preserve
            # settings. For clarity, in 4.1 it was renamed to
            # "preserveSettings". For backwards compatibility with earlier
            # firmware, we can just set them both.
            "backupNetwork":    True
        },
        headers={"apiKey": api_key},
        verify=VERIFY_SERVER_CERT,
        timeout=NORMAL_REQUEST_TIMEOUT)

    if (prepare_response.status_code == 200):
        return True
    else:
        print("ERROR when initializing the firmware update. Status code: " +
              str(prepare_response.status_code))

        if (prepare_response.status_code == 401):
            print('UNAUTHORIZED. Check the API Key.')

    return False

Step 3 - Divide and Upload the Firmware File

To upload a large file to groov EPIC and RIO devices, the file must be broken up into smaller chunks.

By convention, groov Manage uploads firmware files as a series of 25,000,000 byte chunks. There’s nothing special about that exact size, but for consistency, it’s best to do the same.

In Python, this can all be done in memory, by using the file.read() function and passing the bytes directly to the requests.post() function. No temporary files are required.

Each chunk is uploaded with a POST to the /manage/api/v1/maintenance/update/apply endpoint.

Each request needs several custom HTTP Headers, in addition to the normal “apiKey” header:

  • uploader-chunk-number: The current chunk number. It should start at 0 and increment normally, in a serial fashion. Do not upload multiple chunks at one time.
  • uploader-chunks-total: The total number of firmware chunks.
  • uploader-file-id: A unique identifier for this set of uploads. It is used by the server to combine the uploaded chunks.

In addition, the “content-type” HTTP Header is a multipart/form-data type. This part is handled automatically when using the “files” parameter in the call to requests.post().

In the example project, all of this is done in the upload_firmware() function.

# Upload the firmware
def upload_firmware(address, api_key, filename):

    progress.start("Uploading firmware")

    # The firmware must be broken up into manageable chunks.
    # Each chunk is uploaded separately, one after another.
    CHUNK_SIZE = 25000000

    # Determine the number of chunks to be uploaded.
    file_size = os.path.getsize(filename)
    num_chunks = math.ceil(file_size / CHUNK_SIZE)

    # Use a random ID to identify this update. This is used by Groov Manage to
    # reassemble the chunks.
    file_id = random.randint(0, 10000000)

    # Loop over the chunks
    with open(filename, mode='rb') as file:
        chunk_index = -1

        while (True):
            chunk_index += 1

            # Read a chunk of the firmware update file.
            chunk = file.read(CHUNK_SIZE)
            chunk_length = len(chunk)

            # If no data is left, then break out of this loop.
            if (chunk_length == 0):
                progress.succeed()
                return True

            progress.text = f"Uploading firmware (part {chunk_index+1} " \
                f"of {num_chunks})"

            # The "apply" endpoint needs several headers.
            upload_headers = {
                'uploader-chunk-number': str(chunk_index),
                'uploader-chunks-total': str(num_chunks),
                'uploader-file-id': str(file_id),
                'apiKey': api_key
            }

            # POST the file chunk to "/manage/api/v1/maintenance/update/apply"
            apply_response = requests.post(
                "https://" + address +
                "/manage/api/v1/maintenance/update/apply",
                headers=upload_headers,
                files={'file': chunk},
                verify=VERIFY_SERVER_CERT,
                timeout=UPLOAD_REQUEST_TIMEOUT)

            # Check for any errors.
            if (apply_response.status_code >= 400):
                progress.fail()
                print("ERROR - Status code: " +
                      str(apply_response.status_code))
                return False

    progress.fail()

    return False

Step 4 (Optional) - Check the Update’s Status

After the last chunk of the firmware file is uploaded, groov Manage will immediately begin to process the update.

This process typically takes between ten to twenty minutes.

During that time, the /manage/api/v1/maintenance/update/status endpoint can be used to follow the progress. This is useful, but not required. groov Manage will continue to update the firmware regardless of calls to this endpoint.

The update procedure goes through a series of stages. Each stage has its own status property in the returned object.

The order of the stages is:

  1. prepare
  2. upload
  3. reassemble
  4. unzip
  5. decrypt
  6. decompress
  7. backupSettings
  8. install
  9. finishAndRestart

Before being run, the status of a stage will be “pending”.

While being run, the status of a stage will be “running”.

If the stage succeeds, the status will be “done”.

Here’s an example JSON response, showing that most stages are “done”, the “install” stage is currently running, and the “finishAndRestart” stage is still pending:

{
    "prepare":          { "status": "done" },
    "upload":           { "status": "done" },
    "reassemble":       { "status": "done" },
    "unzip":            { "status": "done" },
    "decrypt":          { "status": "done" },
    "decompress":       { "status": "done" },
    "backupSettings":   { "status": "done" },
    "install":          { "status": "running" },
    "finishAndRestart": { "status": "pending" },
    "active":           "active"
}

If the stage fails, the status will be “error”. Additional error properties will be added to the stage. The errorCode property will have an error code, and the errorMessage and errorData properties may have additional information.

Here’s an example JSON response with an error, after uploading RIO firmware to an EPIC unit:

{
    "prepare":          { "status": "done" },
    "upload":           { "status": "done" },
    "reassemble":       { "status": "done" },
    "unzip":            { "status": "done" },
    "decrypt": {
      "status":       "error",
      "errorCode":    8,
      "errorMessage": "Failed to decrypt system update. Confirm that the firmware is correct for this device type."
    },
    "decompress":       { "status": "pending" },
    "backupSettings":   { "status": "pending" },
    "install":          { "status": "pending" },
    "finishAndRestart": { "status": "pending" },
    "active":           "inactive"
}

In the example project, all of this is done in the update_status_until_done() function.

Step 5 (Optional) - Check the Restart and Restore Status

After the firmware has been installed, the device will restart twice. After the first restart, groov Manage restores settings and does other tasks. When done, it restarts the device again.

During that time, the device’s web server is not running. All REST API requests will fail.

After the second restart, the device will start normally. When the device is up and running again, REST API requests will start working again.

If you want to monitor this and know when the device is ready, we recommend using the /auth/access/user/commission/status endpoint. It does not need an API key.

It returns a simple string value, either “true” or “false”.

In the example project, that is handled in the wait_for_restarts() function.