Skip to content

Repository files navigation

SteelClock (Go)

CI codecov Go Report Card License: GPL v3

High-performance display manager for SteelSeries OLED devices written in Go. Supports Apex keyboards (128x40), GameDAC Gen 2 and Arctis Nova Pro base stations (128x64), with multi-device configurations.

VU.mp4

Requirements

Windows

  • Windows 10/11
  • SteelSeries Engine or SteelSeries GG (optional with direct driver)
  • Go 1.21+ (for building from source)

Linux

  • Linux with hidraw support
  • PipeWire or PulseAudio (for audio widgets)
  • GTK 3 and libayatana-appindicator3 (for system tray)
  • Go 1.21+ (for building from source)

Features

  • System Tray Integration: Runs in background with system tray icon
  • Configuration Profiles: Switch between multiple configurations via tray menu
  • Live Configuration Reload: Edit and reload config without restarting
  • Multiple Widgets: Clock, CPU, Memory, Battery, Network, Disk, Keyboard indicators, Keyboard layout, Volume control, Audio visualizer, Bluetooth device status, Winamp integration, Telegram notifications, Claude Code status, Matrix digital rain, Weather, Game of Life, Hyperspace, Star Wars intro
  • Display Modes: Text, horizontal/vertical bars, graphs, analog gauges, etc
  • Per-Core CPU Monitoring: Grid layouts showing individual core usage for all display modes
  • Widget Transparency: Overlay widgets using background_color: -1 for layered displays
  • Gauge Displays: Semicircular analog gauges with needles for CPU/Memory/Volume, dual concentric gauges for Network (RX/TX)
  • Auto-Hide Widgets: Widgets can appear temporarily and hide automatically (ideal for notifications and volume indicators)
  • Volume Control: Real-time Windows system volume monitoring via Core Audio API
  • Low Resource Usage: Minimal CPU and memory footprint (~0.5% CPU, ~15MB RAM)
  • Single Executable: no dependencies, no DLLs required
  • Automatic Logging: All output logged to steelclock.log with timestamps
  • JSON Schema Support: Full IDE autocomplete and validation via included schema file

And it also runs DOOM.

Quick Start

Windows

  1. Build the application:

    build.cmd
    # Or from WSL/bash:
    ./build.sh
    
    # For light build (smaller, excludes telegram widgets):
    build.cmd light
    ./build.sh --light
  2. Run the application:

    steelclock.exe

Linux

  1. Install dependencies:

    sudo apt-get install libgtk-3-dev libayatana-appindicator3-dev
  2. Build the application:

    ./build-linux.sh
    
    # For light build (smaller, excludes telegram widgets):
    ./build-linux.sh --light
  3. Install udev rules for device access (one-time setup):

    sudo cp profiles/99-steelseries.rules /etc/udev/rules.d/
    sudo udevadm control --reload-rules
    sudo udevadm trigger
    # Unplug and replug your keyboard, or reboot
  4. Run the application:

    ./steelclock

The application starts in the background with a system tray icon. Right-click the tray icon to access the menu for switching profiles, editing config, or exiting.

Manual Build

# Build for Windows (GUI mode - no console window)
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -H windowsgui" -o steelclock.exe ./cmd/steelclock

# Build for Linux
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o steelclock ./cmd/steelclock

# Light build (add -tags light to exclude telegram widgets)
go build -tags light -ldflags="-s -w" -o steelclock-light ./cmd/steelclock

Build Variants

SteelClock provides two build variants:

Variant Size Description
Full ~15 MB All widgets included (default)
Light ~10 MB Excludes widgets with external API dependencies

Widgets excluded in light build:

  • telegram - Telegram notifications (requires Telegram API)
  • telegram_counter - Telegram unread counter (requires Telegram API)

To modify the exclusion list, edit cmd/steelclock/imports_light.go.

Command Line Options

-config string
    Path to configuration file (bypasses profile system)

Configuration

The application uses steelclock.json as the main configuration file. The application supports live reload via the tray menu.

For complete configuration documentation, see:

Configuration Profiles

SteelClock supports multiple configuration profiles that can be switched via the tray menu.

Profile System

  • Main config: steelclock.json in the current working directory
  • Profiles: JSON files under profiles/. A profile placed directly in profiles/ belongs to the implicit "General" group; each subdirectory of profiles/ is its own group (see Profile Groups)
  • Profile names: Set via config_name field in JSON, or filename is used as fallback
  • State persistence: The active profile and group — and each group's last-active profile — are saved to .steelclock.state and restored on restart

Profile Groups

Profiles are organized into groups by directory. Each subdirectory of profiles/ is a group, and the tray shows a submenu per group containing only that group's profiles. The main config and any profiles placed directly in profiles/ form an implicit "General" group.

Groups are just folders — use them for whatever you like (themes, use-cases, …) — but the shipped profiles are split by display resolution, since a layout that is pixel-perfect on one screen size is not on another:

profiles/
├── 128x40/   # profiles for Apex keyboards (128x40)
├── 128x64/   # profiles for GameDAC / Nova Pro base stations (128x64)
└── .schema/   # JSON schema (shared)

Pick the group that matches your device from the tray, then choose one of its profiles. Each group remembers its last-active profile, so switching back to a group restores your previous selection.

A profile's $schema reference is relative to the file, so profiles inside a group subdirectory use "../.schema/config.schema.json".

Tray Menu Structure

With a single group, profiles are listed flat. With multiple groups, each group is a submenu containing only its profiles:

128x40 ▸
   Clock (checkmark indicates active)
   CPU
   ...
128x64 ▸
   Clock
   CPU
   ...
─────────
Edit Active Config
Reload Active Config
─────────
Exit

Selecting a profile from any group's submenu switches to it (and makes that group current).

Example Profile Configuration

{
  "$schema": ".schema/config.schema.json",
  "config_name": "My Gaming Profile",
  "refresh_rate_ms": 50,
  "display": { ... },
  "widgets": [ ... ]
}

The config_name field determines how the profile appears in the tray menu. If omitted, the filename (without .json extension) is used.

Supported Widgets

Widget Description Modes Windows Linux
claude_code Claude Code status with Clawd - Yes Yes
clock Current time display text, analog Yes Yes
cpu CPU usage (per-core support) text, bar, graph, gauge Yes Yes
memory RAM usage text, bar, graph, gauge Yes Yes
battery Battery level and charging status text, bar, graph, gauge Yes Yes
bluetooth Bluetooth device status/battery icon, text, bar Yes Yes
network Network I/O (RX/TX) text, bar, graph, gauge Yes Yes
disk Disk I/O (read/write) text, bar, graph Yes Yes
keyboard Lock indicators (Caps/Num/Scroll) icons, text, mixed Yes No
keyboard_layout Current keyboard input language text (ISO 639-1, ISO 639-2, full name) Yes No
volume System volume level and mute text, bar, gauge Yes Yes*
volume_meter Realtime audio peak meter bar, gauge (stereo & VU support) Yes Limited*
audio_visualizer Realtime audio spectrum/waveform spectrum, oscilloscope Yes Yes*
winamp Winamp player info display text (with scrolling support) Yes No
beefweb Foobar2000/DeaDBeeF player text (with scrolling support) Yes Yes
spotify Spotify player info display text (with scrolling support) Yes Yes
telegram Telegram notifications display text (with scrolling/transitions) Yes Yes
telegram_counter Telegram unread message counter text Yes Yes
doom Interactive DOOM game display game Yes Yes
game_of_life Conway's Game of Life simulation - Yes Yes
hyperspace Star Wars hyperspace animation - Yes Yes
starwars_intro Star Wars opening crawl text - Yes Yes
matrix Matrix "digital rain" effect - Yes Yes
hwmon Hardware monitor (LHM/OHM) text, bar, graph, gauge Yes Yes
weather Current weather conditions icon, text Yes Yes

* See Linux Limitations section below.

Note: The beefweb widget requires the beefweb plugin installed in Foobar2000 (Windows) or DeaDBeeF (Linux).

Note: The bluetooth widget requires bqc running locally. The widget polls the bqc REST API to display device connection state, type icon, name, and battery level with configurable low-battery blink threshold.

Note: The spotify widget requires a Spotify Developer Application. See SPOTIFY_README.md for setup instructions.

See CONFIG_GUIDE.md for detailed widget properties and configuration examples.

Supported Devices

Device Family Display Backend Notes
Apex keyboards (7, Pro, 5, etc.) 128x40 direct, gamesense Default mi_01 interface
GameDAC Gen 2 / Arctis Nova Pro (+Omni) 128x64 direct Use direct; GameSense does not drive it*
Arctis Nova 5P 128x64 direct Same protocol as Nova Pro

* The Nova Pro / GameDAC base-station OLED must be driven via the direct backend. The GameSense backend does not drive it — confirmed on the Arctis Nova Pro Omni: SteelSeries GG's own apps use that screen, but third-party GameSense screen events do not reach it. Set "backend": "direct" explicitly for these devices: auto-select tries GameSense first, and because GameSense connects without error the app never falls through to direct, so the OLED stays blank.

For GameDAC setup details, see GAMEDAC_README.md.

Multi-Device Support

SteelClock can drive multiple displays simultaneously (e.g. Apex keyboard + GameDAC). Each device gets its own display settings, backend, and widget set. See GAMEDAC_README.md for configuration examples.

Direct Mode Connection

SteelClock supports two connection backends for communicating with your SteelSeries device:

Backend Options

Backend Description Refresh Rate Requirements
gamesense Uses SteelSeries GG/Engine API 100ms (10 Hz) SteelSeries GG/Engine running
direct Direct USB HID communication ~16-30ms (30-60 Hz) Device VID/PID, udev rules on Linux
(omitted) Auto-select: tries gamesense, then direct Varies -

Configuration

{
  "backend": "direct",
  "direct_driver": {
    "vid": "1038",
    "pid": "1612",
    "interface": "mi_01"
  }
}

If "direct_driver" section is empty, app will try to detect your hardware automatically.

Pros of Direct Mode

  • Higher refresh rates: Up to 60 Hz vs 10 Hz with GameSense
  • Lower latency: Direct USB communication without HTTP overhead
  • No SteelSeries software required: Works without GG/Engine installed
  • Better for real-time visualizations: Audio visualizer, smooth animations
  • Cross-platform: Works on both Windows and Linux

Cons of Direct Mode

  • Device-specific configuration: Need to know VID/PID of your device
  • Exclusive access: May conflict with SteelSeries GG if running simultaneously
  • Limited testing: Only tested with specific SteelSeries keyboards (Apex Pro)
  • Linux requires udev rules: Need to install udev rules for non-root access

Finding Your Device VID/PID

Windows:

  1. Open Device Manager
  2. Find your SteelSeries device under "Human Interface Devices"
  3. Check device properties for VID (Vendor ID) and PID (Product ID)

Linux:

lsusb | grep -i steelseries
# Output: Bus 001 Device 005: ID 1038:1612 SteelSeries ApS SteelSeries Apex Pro
#                                ^^^^ ^^^^
#                                VID  PID

Common values: VID 1038 (SteelSeries), PID varies by model.

Compatibility Notes

  • Direct mode bypasses the GameSense API entirely
  • Some devices may have multiple HID interfaces - use interface to specify (e.g., mi_01)
  • If experiencing issues, omit the backend field to enable auto-selection with fallback
  • Direct mode reconnects automatically if the device is disconnected and reconnected

Linux Limitations

On Linux, some widgets have reduced functionality compared to Windows:

Unsupported Widgets

Widget Reason
keyboard Requires Windows GetKeyState API for lock key detection
keyboard_layout Requires Windows input language API
winamp Winamp is Windows-only software

Limited Functionality

Widget Limitation
volume Uses command-line tools (wpctl, pactl, amixer) instead of native API. Polling-based, not event-driven.
volume_meter Real-time audio peak metering is limited. Falls back to volume level as a proxy when actual audio levels are unavailable.
audio_visualizer Requires PipeWire with parec for audio capture. May need additional configuration for proper audio routing.

Audio Setup on Linux

For audio widgets to work properly on Linux:

  1. PipeWire (recommended):

    # Ensure PipeWire is running
    systemctl --user status pipewire
    
    # Install PipeWire tools if needed
    sudo apt-get install pipewire-audio-client-libraries
  2. PulseAudio:

    # Check PulseAudio is running
    pactl info
  3. Audio capture for visualizer: The audio visualizer captures system audio output. On PipeWire, this should work automatically. On PulseAudio, you may need to configure a monitor source.

Troubleshooting

Application won't start

  • If using gamesense backend: Verify SteelSeries Engine/GG is installed and running
  • If using direct backend: Verify device VID/PID are correct and device is connected
  • Check if steelclock.json exists and is valid JSON
  • Review steelclock.log for initialization errors and stack traces

Config reload fails

  • Check configuration file syntax (must be valid JSON)
  • Verify widget configurations are correct
  • Check steelclock.log for specific validation errors

Linux-specific issues

"Permission denied" when accessing device:

# Install udev rules
sudo cp profiles/99-steelseries.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
# Unplug and replug your keyboard

"cannot read /sys/class/hidraw" error:

  • Ensure the hidraw kernel module is loaded: lsmod | grep hidraw
  • Check if your device appears: ls /dev/hidraw*

System tray icon not appearing:

  • Ensure you have a system tray implementation (e.g., gnome-shell-extension-appindicator for GNOME)
  • Check that libayatana-appindicator3 is installed

Audio widgets not working:

  • Check which audio system is running: pactl info or wpctl status
  • Ensure audio tools are installed: wpctl, pactl, or amixer
  • For audio visualizer, PipeWire is recommended

Logging

All application output is logged to steelclock.log in the same directory as the executable. The log includes:

  • Startup and shutdown events
  • Configuration loading and validation errors
  • Widget initialization
  • GameSense API communication (Windows) / HID communication (Linux)
  • Runtime errors and warnings

Check this file if you encounter any issues or unexpected behavior.

Testing

# Run all tests
go test ./... -cover

# Run with race detection
go test -race ./...

# Run with verbose output
go test ./... -v

# Check coverage
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out

Dependencies

  • github.com/shirou/gopsutil/v4 - System monitoring
  • github.com/getlantern/systray - System tray icon
  • golang.org/x/image - Font rendering and image processing
  • github.com/moutend/go-wca - Windows Core Audio API
  • github.com/go-ole/go-ole - COM interface support for Windows APIs
  • github.com/mjibson/go-dsp - Digital signal processing
  • github.com/go-toast/toast - Windows toast notifications
  • github.com/AndreRenaud/gore - DOOM engine port

License

GNU General Public License v3.0

About

Customizable display manager for SteelSeries devices (Go version).

Topics

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages