You'll begin by copying the following code to a file on your computer called, for example, actions_status.py. (The filename does not really matter, but this page will assume the file is named actions_status.py.)

Click the Copy code link at the top left of the code element below, and paste it into the new file on your computer. For your convenience, this new file should be in the same directory as your .env.sh file, if you used that method to create your personal access token environment variable.

# SPDX-FileCopyrightText: 2022 Tim C for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
GitHub Actions Status Tower Light
"""
import json
import time
import os
import serial
import requests

# Customisations for this program
# Update with the URL from any repo running Actions to get the status. Defaults to CircuitPython.
REPO_WORKFLOW_URL = "https://api.github.com/repos/adafruit/circuitpython/actions/workflows/build.yml/runs"  # pylint: disable=line-too-long
# The rate at which the program will query GitHub for an updated status. You can increase or
# decrease the delay time to fit the duration and frequency of Actions runs on your repo.
# Defaults to 3 minutes.
POLL_DELAY = 60 * 3  # 3 minutes
# The length of time in seconds the LED remains on once the Actions run has completed.
# Defaults to 30 seconds.
COMPLETION_LIGHT_TIME = 30  # seconds
# The length of time in seconds the buzzer beeps once the actions run has completed.
# Set this to 0 to disable the buzzer. Defaults to 1 second.
COMPLETION_BUZZER_TIME = 1  # seconds
# Determines whether the code sends commands to the tower light. Set it to False to disable the
# tower light code and run this example without requiring the tower light. Defaults to True.
ENABLE_USB_LIGHT_MESSAGES = True

# Serial port. Update the serial port to match the port of the tower light on your computer.
# Windows will be a COM** port. If you are on Windows, comment out the Mac/Linux line, and
# uncomment the line immediately below.
# serial_port = "COM57"
# Mac/Linux will be a /dev/** path to the serial port. If you're having trouble finding it,
# check the contents of the /dev/ directory with the tower light unplugged and plugged in.
serial_port = "/dev/tty.usbserial-144430"

# USB Tower Light constants
RED_ON = 0x11
RED_OFF = 0x21
RED_BLINK = 0x41

YELLOW_ON = 0x12
YELLOW_OFF = 0x22
YELLOW_BLINK = 0x42

GREEN_ON = 0x14
GREEN_OFF = 0x24
GREEN_BLINK = 0x44

BUZZER_ON = 0x18
BUZZER_OFF = 0x28
BUZZER_BLINK = 0x48

# Baud rate for serial communication
baud_rate = 9600


def send_command(serialport, cmd):
    serialport.write(bytes([cmd]))


def reset_state():
    # Clean up any old state
    send_command(mSerial, BUZZER_OFF)
    send_command(mSerial, RED_OFF)
    send_command(mSerial, YELLOW_OFF)
    send_command(mSerial, GREEN_OFF)


def buzzer_on_completion():
    if COMPLETION_BUZZER_TIME > 0:
        send_command(mSerial, BUZZER_ON)
        time.sleep(COMPLETION_BUZZER_TIME)
        send_command(mSerial, BUZZER_OFF)


already_shown_ids = []

headers = {'Accept': "application/vnd.github.v3+json",
           'Authorization': f"token {os.getenv('GITHUB_API_TOKEN')}"}


mSerial = None
if ENABLE_USB_LIGHT_MESSAGES:
    print("Opening serial port.")
    mSerial = serial.Serial(serial_port, baud_rate)

print("Starting Github Actions Status Watcher.")
print("Press Ctrl-C to Exit")
try:
    while True:
        print("Fetching workflow run status.")
        response = requests.get(f"{REPO_WORKFLOW_URL}?per_page=1", headers=headers)
        response_json = response.json()
        with open("action_status_result.json", "w") as f:
            f.write(json.dumps(response_json))

        workflow_run_id = response_json['workflow_runs'][0]['id']
        if workflow_run_id not in already_shown_ids:
            status = response_json['workflow_runs'][0]['status']
            conclusion = response_json['workflow_runs'][0]['conclusion']
            print(f"Status - Conclusion: {status} - {conclusion}")

            if status == "queued":
                print("Actions run status: Queued.")
                if ENABLE_USB_LIGHT_MESSAGES:
                    print("Sending serial command 'YELLOW_BLINK'.")
                    send_command(mSerial, YELLOW_BLINK)

            if status == "in_progress":
                print("Actions run status: In progress.")
                if ENABLE_USB_LIGHT_MESSAGES:
                    print("Sending serial command 'YELLOW_ON'.")
                    send_command(mSerial, YELLOW_ON)

            if status == "completed":
                print(f"Adding {workflow_run_id} to shown workflow IDs.")
                already_shown_ids.append(workflow_run_id)

                if conclusion == "success":
                    print("Actions run status: Completed - successful.")
                    if ENABLE_USB_LIGHT_MESSAGES:
                        send_command(mSerial, YELLOW_OFF)
                        print("Sending serial command 'GREEN_ON'.")
                        send_command(mSerial, GREEN_ON)
                        buzzer_on_completion()
                    time.sleep(COMPLETION_LIGHT_TIME - COMPLETION_BUZZER_TIME)
                    if ENABLE_USB_LIGHT_MESSAGES:
                        print("Sending serial command 'GREEN_OFF'.")
                        send_command(mSerial, GREEN_OFF)

                if conclusion == "failure":
                    print("Actions run status: Completed - failed.")
                    if ENABLE_USB_LIGHT_MESSAGES:
                        send_command(mSerial, YELLOW_OFF)
                        print("Sending serial command 'RED_ON'.")
                        send_command(mSerial, RED_ON)
                        buzzer_on_completion()
                    time.sleep(COMPLETION_LIGHT_TIME - COMPLETION_BUZZER_TIME)
                    if ENABLE_USB_LIGHT_MESSAGES:
                        print("Sending serial command 'RED_OFF'.")
                        send_command(mSerial, RED_OFF)

                if conclusion == "cancelled":
                    print("Actions run status: Completed - cancelled.")
                    if ENABLE_USB_LIGHT_MESSAGES:
                        send_command(mSerial, YELLOW_OFF)
                        print("Sending serial command 'RED_BLINK'.")
                        send_command(mSerial, RED_BLINK)
                        buzzer_on_completion()
                    time.sleep(COMPLETION_LIGHT_TIME - COMPLETION_BUZZER_TIME)
                    if ENABLE_USB_LIGHT_MESSAGES:
                        print("Sending serial command 'RED_OFF'.")
                        send_command(mSerial, RED_OFF)

        else:
            print("Already followed the current run.")
        time.sleep(POLL_DELAY)

except KeyboardInterrupt:
    print("\nExiting Github Actions Status Watcher.")
    reset_state()

Code Configuration

Early in the code you'll find a series of options for you to configure. This section will walk through each one and provide details about its purpose, and how you can configure it to fit your needs.

Workflow URL

You can configure the repo workflow URL, which is saved to REPO_WORKFLOW_URL in the code. This is the URL to Actions runs for a specific workflow file on a specific repository. It defaults to the Adafruit CircuitPython repository build.yml file.

REPO_WORKFLOW_URL = "https://api.github.com/repos/adafruit/circuitpython/actions/workflows/build.yml/runs"

There are three elements within the URL that you will likely want to update:

  • User
  • Repository
  • Workflow file

Below is the same line of code with the configurable sections identified.

REPO_WORKFLOW_URL = "https://api.github.com/repos/{USER}/{REPOSITORY}/actions/workflows/{WORKFLOW_FILE}/runs"

To follow Actions statuses on a repository on your own account, you want to replace {USER} with your GitHub user name, {REPOSITORY} with your local repository, and {WORKFLOW_FILE} with the specific workflow file you'd like to use to trigger the status light.

Remember, you must have at least one GitHub Actions workflow enabled on the given repo for this project to work. Workflow files can be found within your repo in the /.github/workflows/ directory.

API Query Interval Duration

You can choose the interval at which the program will query the GitHub API for updates.  POLL_DELAY is a duration in seconds. As shown below, it defaults to 60 * 3 seconds, or three minutes.

POLL_DELAY = 60 * 3

You can increase or decrease this value to fit the typical duration and frequency of Actions runs on your chosen repository. For example, if you usually have short Actions runs that occur every few minutes, you might want to decrease this to 15 or 30 seconds to ensure you catch the statuses of the runs while they're active. If your Actions runs typically take an hour, and occur a few times per day, you may want to increase this to 20 minutes. You can tweak it to figure out what works best for you.

Status Light Duration

You can choose the duration the light will remain on after getting a completed status response from the GitHub API query. COMPLETION_LIGHT_TIME is a duration in seconds, which, as shown below, defaults to 30.

COMPLETION_LIGHT_TIME = 30

When a completed status is returned from the API for the current Actions run, the light will respond by turning on red or green depending on the conclusion. The red or green light will stay on for the duration you choose here.

Increase or decrease this value to fit what works best for you.

Buzzer Sound Duration

You can choose the duration the buzzer will sound after getting a completed status response from the API query. COMPLETION_BUZZER_TIME is a duration in seconds, which, as shown below, defaults to 1.

COMPLETION_BUZZER_TIME = 1
The buzzer is incredibly loud! Be prepared for this when running this code. You can set this variable to 0 to disable the buzzer completely.

When a completed status is returned from the API for the current Actions run, the buzzer will sound for both passing or failing conclusions, e.g. the buzzer sounds when the light turns on red or green. The duration of the buzzer is configurable by you.

You can set this variable to 0 to disable the buzzer completely, if you'd rather.

Enable USB Tower Light

This code is intended to be run with a USB Tower Light attached to your computer. However, it was written to be able to run without the hardware involved. The code defaults to expecting the hardware, with the following variable being set to True.

ENABLE_USB_LIGHT_MESSAGES = True

If you simply want to test whether your setup is working to enable you access to the GitHub Actions API, you can set ENABLE_USB_LIGHT_MESSAGES = False to disable sending the light commands in the code. Statuses will be printed to the serial console if this is disabled.

Serial Port

When you connect the tower light to your computer via USB (as long as you've installed the drivers!), it will show up on a serial port. You'll want to identify what that port is, and update serial_port to match.

Windows

On Windows, the serial port will be COM**, where ** is a number. Click here if you need help figuring out where to find the COM port. Once identified, you'll need to do two things in the code. First, comment out the Mac/Linux serial_port = line by adding a # to the beginning of the line. Second, uncomment the Windows serial_port = line, and update it to match the COM port from your tower light.

Your code would look similar to the following.

serial_port = "COM57"
# Mac/Linux will be a /dev/** path to the serial port. (...)
# serial_port = "/dev/tty.usbserial-144430"

MacOS/Linux

On MacOS and Linux, the serial port will be a /dev/** path. If you're struggling to find it, check the contents of /dev/ with the tower light unplugged, and then check them again with the light plugged in. The new item in /dev/ will most likely be the tower light serial port. Once Identified, update the serial_port = line to match the serial port from your tower light.

On MacOS, your code would look similar to the default code.

On Linux, your code may look similar to the following.

serial_port = "/dev/USBserial0"

That's it to configure! Now you're ready to run the code.

This guide was first published on Jul 06, 2022. It was last updated on 2022-07-06 09:21:47 -0400.

This page (Code Configuration) was last updated on Aug 01, 2022.

Text editor powered by tinymce.