Mercury
"swift messenger"
BLE client daemon written in go. scans for janus, connects over bluetooth low energy, reads GATT characteristics, and pushes data to redis. the invisible bridge between your phone and the car thing. fast, resilient, reliable.
data flow
┌─────────────┐ BLE ┌──────────────┐
│ Janus │◄──────────────────────►│ Mercury │
│ (Android) │ GATT Protocol │ (Go daemon) │
│ │ │ │
│ Advertises: │ │ Scans for: │
│ "Janus" │ │ "Janus" │
│ │ │ │
│ Exposes: │ Reads: │ │
│ 0x2001 ────┼────────► Track Title │ │
│ 0x2002 ────┼────────► Artist │ │
│ 0x2003 ────┼────────► Album │ │
│ 0x2004 ────┼────────► State/Position │ │
│ 0x2005 ────┼────────► Album Art │ │
│ 0x2006 ────┼────────► Lyrics │ │
└─────────────┘ └──────┬───────┘
│
│ Redis Protocol
│ (localhost:6379)
▼
┌──────────────┐
│ Redis │
│ Data Store │
│ │
│ Keys: │
│ media:title │
│ media:artist│
│ media:album │
│ media:state │
│ media:art │
│ media:lyrics│
└──────────────┘mercury acts as a translator between the BLE world and the redis world. it continuously polls janus for updates and pushes changes to redis where llizard plugins can read them.
features
intelligent scanning
scans for BLE devices advertising as "Janus". caches the MAC address for faster reconnection. stops scanning once connected to save battery. resumes scanning if connection drops.
chunked album art
BLE has a 512-byte MTU limit. janus splits album art into chunks. mercury reads all chunks sequentially, reassembles the base64 string, and stores it in redis as a complete image.
automatic reconnection
handles bluetooth signal loss gracefully. retries connection with exponential backoff. clears stale data from redis when disconnected. notifies llizard plugins of connection state changes.
implementation
go libraries
tinygo-org/bluetooth- BLE stack for Linuxgo-redis/redis- Redis clientsirupsen/logrus- Structured logging
polling interval
mercury polls janus characteristics every 500ms. this balances responsiveness with battery usage. characteristics that change frequently (playback position) are cached with a 250ms invalidation window to reduce BLE traffic.
redis keys
| Redis Key | Value Type | Example |
|---|---|---|
| media:title | string | "Bohemian Rhapsody" |
| media:artist | string | "Queen" |
| media:album | string | "A Night at the Opera" |
| media:state | JSON | {playing: true, pos: 124} |
| media:albumart | base64 | iVBORw0KGgoAAAA... |
| media:lyrics | LRC | [00:12.00]Is this... |
building mercury
mercury can be built for desktop (development) or cross-compiled for the car thing (ARMv7).
desktop build
# Install dependencies (Linux)
sudo apt install bluez libbluetooth-dev
# Build
go build -o mercury ./cmd/mercury
# Run
./mercurycross-compile for car thing
# Set environment for ARMv7
export GOOS=linux
export GOARCH=arm
export GOARM=7
export CC=arm-linux-musleabihf-gcc
export CGO_ENABLED=1
# Build
go build -o mercury-arm ./cmd/mercury
# Copy to device
scp mercury-arm root@172.16.42.2:/usr/local/bin/mercuryconfiguration
mercury reads configuration from /etc/mercury.conf
# BLE settings
SCAN_TIMEOUT=30s
POLL_INTERVAL=500ms
RECONNECT_BACKOFF=5s
# Redis settings
REDIS_HOST=localhost:6379
REDIS_PASSWORD=
REDIS_DB=0
# Logging
LOG_LEVEL=info
LOG_FILE=/var/log/mercury/currenttroubleshooting
mercury can't find janus
- • ensure janus app is running on your android device
- • check that bluetooth is enabled on both devices
- • verify janus has notification listener permission
- • try restarting bluetooth:
sv restart bluetooth
connection keeps dropping
- • BLE has limited range (~10 meters). move devices closer
- • check for bluetooth interference from other devices
- • verify phone isn't in aggressive battery saving mode
no data in redis
- • check mercury logs:
tail -f /var/log/mercury/current - • verify redis is running:
sv status redis - • test redis connection:
redis-cli ping