This is a Rust-based MQTT bridge for vcontrold (Viessmann heating controller interface). It runs as a Docker container that:
- Manages the vcontrold daemon lifecycle
- Maintains a persistent TCP connection to vcontrold (port 3002)
- Polls heating controller parameters periodically
- Publishes values to MQTT topics
- Provides a request/response bridge via MQTT
┌─────────────────────────────────────────────────────────┐
│ Docker Container │
│ │
│ ┌─────────────────┐ ┌─────────────────────────────┐│
│ │ vcontrold │ │ vcontrold-mqttd (Rust) ││
│ │ (daemon) │◄──►│ ││
│ │ Port 3002 │TCP │ ┌─────────┐ ┌──────────┐ ││
│ └─────────────────┘ │ │ Polling │ │Subscriber│ ││
│ │ │ │ Loop │ │ Task │ ││
│ ▼ │ └────┬────┘ └────┬─────┘ ││
│ ┌─────────────────┐ │ │ │ ││
│ │ /dev/vitocal │ │ ▼ ▼ ││
│ │ (USB serial) │ │ ┌─────────────────────┐ ││
│ └─────────────────┘ │ │ MQTT Client │ ││
│ │ │ (rumqttc v5) │ ││
│ │ └──────────┬──────────┘ ││
│ └─────────────┼───────────────┘│
└───────────────────────────────────────┼────────────────┘
│
▼
┌─────────────────┐
│ MQTT Broker │
└─────────────────┘
src/
├── main.rs # Entry point, tokio runtime, task orchestration
├── config.rs # Environment variable parsing
├── error.rs # Error types (thiserror)
├── polling.rs # Command batching, periodic execution
├── process.rs # Spawn/monitor vcontrold daemon
├── vcontrold/
│ ├── mod.rs
│ ├── client.rs # Persistent TCP connection with reconnect
│ └── protocol.rs # Protocol constants, response parsing
└── mqtt/
├── mod.rs
├── client.rs # MQTT v5 client with TLS support
├── publisher.rs # Publish polling results to topics
└── subscriber.rs # Request/response bridge
We use direct TCP communication to vcontrold instead of shelling out to vclient:
- Single persistent connection (reduces latency)
- No process spawning overhead
- Better error handling and reconnection logic
Simple text protocol over TCP:
- Send:
command\n - Receive:
value unit\nfollowed byvctrld>prompt - Errors: Lines starting with
ERR: - Quit:
quit\n->good bye!\n
- Library: rumqttc with MQTT v5 protocol
- TLS: rustls (not OpenSSL) for smaller binary and easier cross-compilation
- Single client with shared access via Arc
- Separate event loop task handles connection management
# Build dev image
docker build -t vcontrold-mqttd-dev -f .devcontainer/Dockerfile .devcontainer
# Run tests
docker run --rm -v "$(pwd)":/workspace -w /workspace vcontrold-mqttd-dev cargo test
# Interactive development
docker run --rm -it -v "$(pwd)":/workspace -w /workspace vcontrold-mqttd-dev bashdocker build -t vcontrold-mqttd:latest .cargo testCurrent tests cover:
- Command batching algorithm
- Protocol response parsing
- JSON response building
- Numeric value formatting
Requires a running vcontrold instance or mock server. The devcontainer includes:
- vcontrold binary
- mosquitto broker (localhost:1883)
- Add field to appropriate struct in
src/config.rs - Parse in
Config::from_env()orMqttConfig::from_env() - Update SPEC.md with the new variable
- Protocol constants:
src/vcontrold/protocol.rs - Connection/command execution:
src/vcontrold/client.rs
- Client setup/TLS:
src/mqtt/client.rs - Publishing logic:
src/mqtt/publisher.rs - Request/response:
src/mqtt/subscriber.rs
Key crates:
tokio- Async runtimerumqttc- MQTT v5 client (with rustls TLS via tokio-rustls)rustlsv0.23 - TLS implementation (used directly forClientConfigbuilding; must be compatible with tokio-rustls version used by rumqttc)serde/serde_json- JSON serializationtracing- Structured loggingthiserror- Error type derivation
-
rustls version: rumqttc 0.25 uses tokio-rustls 0.26, which depends on rustls 0.23. The direct
rustlsdependency in Cargo.toml (used for buildingClientConfiginmqtt/client.rs) must resolve to the same major version. -
Transport enum:
rumqttc::Transportis at crate root, not inrumqttc::v5. -
Publish lifetime: rumqttc's publish requires owned data (
Vec<u8>), not borrowed slices. -
tracing macro shadowing: Don't name variables
debug,info, etc. - they conflict with tracing macros.
- Never auto-push to remote
- Commit by topic when committing without explicit approval
- Amend existing commits by topic when modifying after committing
- Ask before destructive git operations