Skip to content

jaydenk/igrill-remote-server

Repository files navigation

iGrill Remote Server

Originally derived from bendikwa/esphome-igrill by Bendik Wang Andreassen.

Overview

A standalone BLE polling service for Weber iGrill thermometer devices. It continuously scans for and connects to iGrill devices over Bluetooth Low Energy, exposes real-time temperature data via an HTTP + WebSocket API, and serves a single-page web dashboard for live monitoring.

Supported Devices

  • iGrill mini
  • iGrill mini V2
  • iGrill V2
  • iGrill V202
  • iGrill V3
  • iDevices Kitchen Thermometer
  • Weber Pulse 1000
  • Weber Pulse 2000

Quick Start

Docker Compose (recommended)

The image is built automatically by CI and published to the GitHub Container Registry.

cp env.example .env            # edit values as needed
docker compose up -d
curl http://localhost:39120/health

To update to the latest image:

docker compose pull && docker compose up -d

By default the container exposes the port directly. To place the service behind a Traefik reverse proxy, create a docker-compose.override.yml (gitignored) alongside the base compose file:

services:
  igrill:
    ports: !reset []
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.igrill.rule=Host(`igrill.${HOSTNAME}`)"
      - "traefik.http.routers.igrill.entrypoints=web-secure"
      - "traefik.http.routers.igrill.tls.certresolver=myresolver"
      - "traefik.http.services.igrill.loadbalancer.server.port=39120"
    networks:
      - proxy

networks:
  proxy:
    external: true

Ensure the external network exists: docker network create proxy

Local Development

pip install -r requirements-dev.txt
python -m service.main

The server binds to 0.0.0.0:39120 by default. All configuration is via environment variables (see below).

Configuration

Copy env.example to .env and edit values as needed. All variables are optional and have sensible defaults.

Variable Default Description
IGRILL_PORT 39120 HTTP server port.
IGRILL_POLL_INTERVAL 15 BLE polling interval in seconds (clamped to 5-60).
IGRILL_TIMEOUT 30 GATT characteristic read timeout in seconds.
IGRILL_CONNECT_TIMEOUT 10 BLE connection timeout in seconds (separate from read timeout).
IGRILL_MAX_BACKOFF 60 Maximum exponential backoff delay in seconds between reconnection attempts.
IGRILL_SCAN_INTERVAL 60 Time between BLE discovery scans in seconds.
IGRILL_SCAN_TIMEOUT 5 Duration of each BLE discovery scan in seconds.
IGRILL_RECONNECT_GRACE 60 Seconds within which a reconnecting device reuses its existing session membership.
IGRILL_DB_PATH /data/igrill.db SQLite database path for persisted sessions and readings.
IGRILL_MAC_PREFIX 70:91:8F MAC address prefix used to filter devices during scans.
IGRILL_BIND_ADDRESS 0.0.0.0 HTTP server bind address.
IGRILL_LOG_LEVEL INFO Global log level (DEBUG, INFO, WARNING, ERROR).
IGRILL_LOG_LEVEL_BLE (global) Override log level for the BLE subsystem (igrill.ble).
IGRILL_LOG_LEVEL_WS (global) Override log level for the WebSocket subsystem (igrill.ws).
IGRILL_LOG_LEVEL_SESSION (global) Override log level for the session/history subsystem (igrill.session).
IGRILL_LOG_LEVEL_ALERT (global) Override log level for the alert subsystem (igrill.alert).
IGRILL_LOG_LEVEL_HTTP (global) Override log level for the HTTP subsystem (igrill.http).
IGRILL_SESSION_TOKEN (empty) If set, requires Authorization: Bearer <token> on WebSocket session-control messages.
IGRILL_CORS_ORIGIN (empty) If set, adds CORS Access-Control-Allow-Origin headers (e.g. * for development). A warning is logged if set to *.

API Reference

REST Endpoints

Method Path Description
GET / Web dashboard — tab-based single-page UI (Live, History, Settings) with real-time WebSocket updates, session controls, BLE state indicators, live temperature charts (uPlot), past session browsing with full-timeline charts and summary statistics, and runtime log level management.
GET /health Health check with uptime, device counts, active session ID, poll interval, and scan interval.
GET /api/sessions Paginated session list (?limit=20&offset=0).
GET /api/sessions/{id} Session detail with devices, targets, and readings. Returns 404 if the session does not exist.
PUT /api/config/log-levels Runtime log level update (requires authorisation).

WebSocket Protocol (v2)

Connect to /ws for real-time streaming. All messages use the v2 envelope format:

{"v": 2, "type": "<msg_type>", "ts": "...", "requestId": "...", "payload": {}}

Client request types:

Type Description
status_request Returns device state, session info, sample rate, active targets, and session devices.
sessions_request Lists recent sessions (payload.limit defaults to 20, max 100).
history_request Streams history chunks (sinceTs, untilTs, limit (max 10,000), sessionId, chunkSize).
session_start_request Starts a new user-initiated session. Accepts optional targets array and deviceAddresses (array) or deviceAddress (string). If no devices are specified, all currently connected devices are included. Requires authorisation.
session_end_request Ends the current session. Requires authorisation.
session_add_device_request Adds a device to the active session mid-cook. Requires deviceAddress in payload. Requires authorisation.
target_update_request Updates targets for the current session. Accepts optional deviceAddress to scope targets to a specific device. Requires authorisation.

Server response types:

Type Description
status Response to status_request with device state, session info, sample rate, active targets, and session devices.
sessions_list Response to sessions_request with recent session summaries.
history_chunk / history_end Streamed response to history_request.
session_start_ack Acknowledgement for session_start_request.
session_end_ack Acknowledgement for session_end_request.
target_update_ack Acknowledgement for target_update_request.
session_add_device_ack Acknowledgement for session_add_device_request.

Server broadcast types:

Type Description
reading Pushed on each poll cycle with latest probe data (always broadcast, regardless of session state).
session_start / session_end Broadcast when sessions change.
device_joined Broadcast when a device is added to an active session.
target_approaching Probe temperature crossed the pre-alert threshold.
target_reached Probe temperature hit the target.
target_exceeded Probe temperature went above the target.
target_reminder Periodic nudge while temperature remains above target.
device_state_change Broadcast when a device's connection state changes (e.g. connecting, polling, disconnected, backoff).

Note: curl does not support WebSockets. Use a client such as websocat, wscat, or an iOS URLSessionWebSocketTask.

Architecture

BLE Connection State Machine

Each device worker manages a six-state connection lifecycle: discovered -> connecting -> authenticating -> polling -> disconnected -> backoff -> connecting (retry). On disconnect or error, the worker uses exponential backoff (starting at 2 seconds, capped at IGRILL_MAX_BACKOFF) before attempting reconnection. A successful connection resets the backoff counter. Authentication is retried up to three times before failing. Probe readings are zeroed on disconnect to avoid displaying stale data.

User-Initiated Sessions

Sessions are user-initiated only — no session is auto-created on startup or when a device connects. The device worker always polls BLE and broadcasts live readings to WebSocket clients, but only records to the database and evaluates alert targets when a session is active and the device is part of it.

Multi-Device Session Support

A single session can include multiple iGrill devices. Devices can be added to an active session at any time via session_add_device_request. When a device disconnects during a session, it is marked as having left; on reconnect within the grace period, it is automatically rejoined.

Normalised Data Layer

Session data is stored in a normalised SQLite schema: sessions, session-device membership, per-probe readings, and per-device targets are all separate tables linked by foreign keys with UUID session identifiers. Schema changes are applied automatically via a sequential migration runner on startup.

Post-Session Downsampling

When a session ends, the raw readings are downsampled to reduce storage. Both probe readings and device readings (battery, propane, heating) are cleaned up together so that historical queries remain consistent. This preserves the overall shape of the temperature curve while significantly reducing database size for long cooks.

Device Manager Health Monitoring

The device manager monitors worker health on each scan cycle. If a worker task crashes due to an unhandled exception, it is automatically respawned and the device store is updated with an error status.

Project Structure

service/
  __init__.py
  config.py              # Centralised configuration from environment variables
  logging_setup.py       # Structured logging with per-subsystem level control
  main.py                # App factory and entry point
  alerts/
    evaluator.py         # Checks probes against targets, emits alert events
  api/
    envelope.py          # WebSocket v2 message envelope construction
    routes.py            # HTTP route handlers and route registration
    websocket.py         # WebSocketHub, WebSocketClient, and v2 protocol handler
  ble/
    protocol.py          # BLE protocol constants, model definitions, and detection
    connection_state.py  # ConnectionStateMachine with exponential backoff
    device_worker.py     # Connects, authenticates, and polls a single iGrill device
    device_manager.py    # Scans for iGrill devices and spawns/monitors workers
  db/
    schema.py            # Normalised database schema definitions and init_db()
    migrations.py        # Sequential schema migration runner
  history/
    downsampler.py       # Post-session reading downsampling
    store.py             # SQLite-backed sessions, readings, and targets
  models/
    device.py            # DeviceStore — async-safe in-memory device state
    reading.py           # Temperature probe parsing and reading payload builder
    session.py           # TargetConfig dataclass for probe target temperatures
  web/
    dashboard.py         # Dashboard route handler and static file serving
    static/
      index.html         # Tab-based monitoring dashboard with live view, session history with full-timeline charts and summary statistics, settings with device info and runtime log level controls (vanilla HTML/CSS/JS, uPlot)
tests/
  conftest.py            # Shared pytest fixtures
  test_alerts.py         # AlertEvaluator tests
  test_config.py         # Configuration module tests
  test_config_new.py     # Extended configuration tests
  test_connection_state.py  # ConnectionStateMachine tests
  test_downsampler.py    # Post-session downsampling tests
  test_history_store.py  # HistoryStore tests
  test_logging.py        # Structured logging tests
  test_models.py         # Data models tests
  test_protocol.py       # BLE protocol module tests
  test_integration.py    # Full-server integration tests
  test_routes.py         # HTTP route handler tests
  test_schema.py         # Database schema tests

Development

Running Tests

pip install -r requirements-dev.txt
python -m pytest tests/ -v

Docker Build

docker compose build
docker compose up -d

CI/CD

A GitHub Actions workflow (.github/workflows/ci.yml) runs on every push and pull request to main:

  1. Test — installs dependencies and runs pytest on Ubuntu.
  2. Docker — on merge to main, builds and pushes the Docker image to ghcr.io/jaydenk/igrill-remote-server:latest (and a SHA-tagged variant).

To pull the pre-built image instead of building locally:

docker pull ghcr.io/jaydenk/igrill-remote-server:latest

BLE Host Requirements

  • The host must run BlueZ. Mount /run/dbus into the container and set DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket.
  • The container must be able to access the host Bluetooth adapter (runs as root in Docker by default).
  • BLE devices accept only one connection at a time — disconnect the mobile app before connecting the server.

About

iGrill BLE thermometer monitoring server — Python/aiohttp with WebSocket API

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors