# Selsyn Gauge MQTT Controller

MQTT-based gauge controller for ESP32 / MicroPython. Drives a VID28/BKA30D/X25 stepper gauge and integrates with Home Assistant via MQTT.

## Files

| File | Description |
|------|-------------|
| `boot.py` | WiFi connect + OTA trigger |
| `ota.py` | Gitea API poller for OTA updates |
| `ota_config.json` | OTA config (create from `ota_config.example.json`) |
| `ota_config.example.json` | OTA config template |
| `gaugemqttcontinuous.py` | Main MQTT controller |
| `gauge_vid6008.py` | Stepper driver for VID28/BKA30D/X25 gauges |
| `main.py` | Entry point, calls `ota.mark_ok()` on success |
| `config.json` | Application config (create from `config.example.json`) |
| `config.example.json` | Template with all options |

## Deployment

```bash
# First time: upload via USB
ampy --port /dev/ttyUSB0 put boot.py
ampy --port /dev/ttyUSB0 put ota.py
ampy --port /dev/ttyUSB0 put ota_config.json
ampy --port /dev/ttyUSB0 put main.py
ampy --port /dev/ttyUSB0 put gaugemqttcontinuous.py
ampy --port /dev/ttyUSB0 put gauge_vid6008.py
ampy --port /dev/ttyUSB0 put config.json

# Monitor serial output
minicom -D /dev/ttyUSB0 -b 115200
```

## MQTT Topics

All topics are prefixed with `mqtt_prefix`:

| Topic | Direction | Description |
|-------|-----------|-------------|
| `/set` | ← | Target value from HA |
| `/state` | → | Current value |
| `/status` | → | `online`/`offline` |
| `/zero` | ← | Trigger zero calibration |
| `/led/red/set` | ← | `ON`/`OFF` |
| `/led/red/state` | → | `ON`/`OFF` |
| `/led/green/set` | ← | `ON`/`OFF` |
| `/led/green/state` | → | `ON`/`OFF` |
| `/led/backlight/set` | ← | `{"state": "ON"/"OFF", "color": {"r","g","b"}, "brightness": 0-255}` |
| `/led/backlight/state` | → | Current backlight state |

## Configuration

Copy `config.example.json` to `config.json` and `ota_config.example.json` to `ota_config.json` and edit:

### WiFi

WiFi credentials are in `ota_config.json` (shared with OTA settings), not `config.json`.

### MQTT

| Key | Default | Description |
|-----|---------|-------------|
| `mqtt_broker` | — | Broker IP/hostname |
| `mqtt_port` | 1883 | Broker port |
| `mqtt_user` | — | MQTT username |
| `mqtt_password` | — | MQTT password |
| `mqtt_client_id` | — | Unique client ID |
| `mqtt_prefix` | — | Topic prefix for all MQTT messages |

### Gauge

| Key | Default | Description |
|-----|---------|-------------|
| `gauge_pins` | `[12, 13]` | GPIO pins (`4phase`: IN1-4, `stepdir`: DIR, STEP) |
| `gauge_mode` | `stepdir` | Driver mode: `stepdir` (DIR+STEP) or `4phase` (full-wave) |
| `gauge_min` | 0 | Minimum value |
| `gauge_max` | 7300 | Maximum value |
| `gauge_pulse` | 4 | Pulse width in milliseconds |
| `gauge_half_step` | false | Use 8-phase half-step mode (1200 steps, smoother) |
| `smooth_step_ms` | 50 | Smooth movement interval |
| `idle_release_ms` | 3000 | Release coils after idle (ms) |
| `rezero_interval_ms` | 3600000 | Auto-rezero interval (ms, default 1hr) |

### OTA

| Key | Default | Description |
|-----|---------|-------------|
| `gitea_base` | — | Gitea server URL (no trailing slash) |
| `repo_owner` | — | Repository owner |
| `repo_name` | — | Repository name |
| `repo_folder` | — | Folder inside repo to sync |
| `repo_branch` | `main` | Branch to track |
| `api_token` | — | API token for private repos |
| `wifi_ssid` | — | WiFi network name |
| `wifi_password` | — | WiFi password |

### LEDs

| Key | Default | Description |
|-----|---------|-------------|
| `led_red_pin` | 33 | Red LED GPIO pin |
| `led_green_pin` | 32 | Green LED GPIO pin |
| `led_bl_pin` | 23 | RGB backlight (WS2812/NeoPixel) GPIO pin |

### Home Assistant Discovery

| Key | Default | Description |
|-----|---------|-------------|
| `device_name` | "Selsyn 1" | Device name in HA |
| `device_model` | "Chernobyl Selsyn-inspired gauge" | Device model |
| `device_manufacturer` | "AdeBaumann" | Device manufacturer |
| `device_area` | "Control Panels" | Suggested area in HA |
| `gauge_entity_name` | "Selsyn 1 Power" | Gauge entity name |
| `gauge_unit` | "W" | Unit of measurement for gauge |
| `red_led_entity_name` | "Selsyn 1 Red LED" | Red LED entity name |
| `green_led_entity_name` | "Selsyn 1 Green LED" | Green LED entity name |
| `backlight_entity_name` | "Selsyn 1 Backlight" | Backlight entity name |
| `backlight_unit` | "%" | Unit for backlight |

### Timing

| Key | Default | Description |
|-----|---------|-------------|
| `heartbeat_ms` | 10000 | State publish interval (ms) |

## OTA Updates

On each boot, `boot.py` connects WiFi and runs `ota.update()`. The updater:

1. If last boot was good and commit unchanged → skip file check entirely
2. Otherwise, compares file SHA1 hashes with local manifest
3. Downloads changed/missing files to `.tmp`, then renames into place
4. On success, saves manifest with commit SHA; `main.py` writes OK flag

### Manifest

Create `ota_manifest.txt` in your repo root to specify which files to sync:

```
boot.py           # specific file
main.py           # another file
*.py              # wildcard (matches anywhere)
selsyn/           # entire directory (trailing slash)
```
