Skip to content

Guides

Practical guides for integrating, deploying, and operating vito2mqtt.


Home Assistant Integration

vito2mqtt publishes telemetry as JSON to MQTT topics that Home Assistant can consume directly via the MQTT integration.

Temperature sensors

configuration.yaml
mqtt:
  sensor:
    - name: "Outdoor Temperature"
      state_topic: "vito2mqtt/vitodens200w/outdoor/state"
      value_template: "{{ value_json.outdoor_temperature }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

    - name: "Hot Water Temperature"
      state_topic: "vito2mqtt/vitodens200w/hot_water/state"
      value_template: "{{ value_json.hot_water_temperature }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

    - name: "Boiler Temperature"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.boiler_temperature }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

    - name: "Exhaust Temperature"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.exhaust_temperature }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

Burner statistics

configuration.yaml
mqtt:
  sensor:
    - name: "Burner Starts"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.burner_starts }}"
      state_class: total_increasing

    - name: "Burner Hours"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.burner_hours_stage1 }}"
      unit_of_measurement: "h"
      state_class: total_increasing

    - name: "Burner Modulation"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.burner_modulation }}"
      unit_of_measurement: "%"

    - name: "Plant Power Output"
      state_topic: "vito2mqtt/vitodens200w/burner/state"
      value_template: "{{ value_json.plant_power_output }}"
      unit_of_measurement: "%"

Hot water setpoint control

configuration.yaml
mqtt:
  number:
    - name: "Hot Water Setpoint"
      command_topic: "vito2mqtt/vitodens200w/hot_water/set"
      command_template: '{"hot_water_setpoint": {{ value }}}'
      min: 30
      max: 60
      step: 1
      unit_of_measurement: "°C"
      device_class: temperature

No telemetry feedback for hot_water_setpoint

The hot_water_setpoint signal is not included in the hot_water telemetry group (which only publishes hot_water_temperature and hot_water_outlet_temperature). It is available via the command interface, but its current value is not published in MQTT telemetry, so the HA number entity will not show the current boiler value based on telemetry alone. To track the current setpoint, you can either read it explicitly using the command interface or rely on HA's optimistic mode.

Error status

configuration.yaml
mqtt:
  binary_sensor:
    - name: "Boiler Error"
      state_topic: "vito2mqtt/vitodens200w/diagnosis/state"
      value_template: "{{ value_json.error_status }}"
      payload_on: 1
      payload_off: 0
      device_class: problem

Docker Deployment

Docker Compose

compose.yml
services:
  vito2mqtt:
    image: ghcr.io/ff-fab/vito2mqtt:latest
    restart: unless-stopped
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
    env_file:
      - .env
    environment:
      - VITO2MQTT_SERIAL_PORT=/dev/ttyUSB0
      - VITO2MQTT_MQTT__HOST=mosquitto
      - VITO2MQTT_MQTT__TLS=${VITO2MQTT_MQTT__TLS:-false}
    depends_on:
      - mosquitto

  mosquitto:
    image: eclipse-mosquitto:2
    restart: unless-stopped
    ports:
      - "127.0.0.1:1883:1883"
    volumes:
      - mosquitto-data:/mosquitto/data
      - mosquitto-config:/mosquitto/config

volumes:
  mosquitto-data:
  mosquitto-config:

Device permissions

The container needs access to the serial device. Ensure the container user has read/write permissions on /dev/ttyUSB0. You may need to add the user to the dialout group or use privileged: true (not recommended for production).

Dockerfile (from source)

Dockerfile
FROM python:3.14-slim

RUN pip install uv
WORKDIR /app
COPY . .
RUN uv sync --no-dev

CMD ["uv", "run", "vito2mqtt"]

Common Recipes

Adjusting hot water temperature

Publish a JSON payload to the hot water command topic:

# Set hot water target to 55°C
mosquitto_pub -h localhost \
  -t 'vito2mqtt/vitodens200w/hot_water/set' \
  -m '{"hot_water_setpoint": 55}'

The read-before-write optimization checks the current value first. If the boiler is already set to 55°C, no write occurs. Use "__force": true to write regardless.

Setting heating schedule timers

Timer signals use the CycleTime (CT) format — 4 on/off slot pairs per day:

# Set Monday radiator schedule: heat 06:00–08:30 and 17:00–22:00
mosquitto_pub -h localhost \
  -t 'vito2mqtt/vitodens200w/heating_radiator/set' \
  -m '{
    "timer_m1_monday": [
      [[6, 0], [8, 30]],
      [[17, 0], [22, 0]],
      [[null, null], [null, null]],
      [[null, null], [null, null]]
    ]
  }'

Unused slots must be [null, null]. Each day has exactly 4 pairs of 2 time slots, where each slot is [hours, minutes] with int or null elements.

What to expect after a write

Commands are never acknowledged on their own topic — the boiler's state is published by telemetry only. Since a successful write now wakes its own telemetry group, that refresh happens within seconds instead of at the next poll (which for the system group is an hour by default).

Two things are worth knowing before you go looking for a value:

  • A group's /set signals are not the same signals as its /state signals. Writing heating_curve_gradient_m1 does not make that value appear on heating_radiator/state; it changes what the boiler reports for flow_temperature_setpoint_m1. You are watching for the boiler's reaction, not for an echo.
  • Nothing is published if nothing changed. State is published on change, so a write the boiler has not yet acted on produces no message until it has, or until the next scheduled poll.

Writes that the read-before-write guard suppresses do not trigger a refresh at all — there was no write. Bursts of writes are throttled to one extra read per group per 15 seconds so a full weekly schedule (seven payloads) cannot saturate the serial bus; the last write is still picked up.

Checking error history

Subscribe to the diagnosis state topic:

mosquitto_sub -h localhost \
  -t 'vito2mqtt/vitodens200w/diagnosis/state' -v

Error history entries (ES type) are returned as objects with error and timestamp fields, where error is a human-readable description:

{
  "error_status": 0,
  "error_history_1": { "error": "Flame loss during operation", "timestamp": "2026-01-15T14:30:00" },
  "error_history_2": { "error": "Flow sensor fault", "timestamp": "2025-12-03T08:15:00" }
}

A value of 0 for error_status means no active error.

Monitoring burner efficiency

Track burner starts and operating hours to assess cycling behavior:

mosquitto_sub -h localhost \
  -t 'vito2mqtt/vitodens200w/burner/state' -v

Key metrics:

  • burner_starts — Total number of burner ignitions. High starts/hour indicates short cycling.
  • burner_hours_stage1 — Total stage 1 operating hours.
  • burner_modulation — Current modulation percentage (0–100%). Low sustained modulation suggests oversized output.
  • plant_power_output — Overall power output percentage.

Efficiency tracking

Calculate the average run time per start: burner_hours_stage1 / burner_starts. Values below 5 minutes suggest the boiler is short-cycling, which reduces efficiency and increases wear.


Troubleshooting

Serial port permission denied

Symptom: PermissionError: [Errno 13] Permission denied: '/dev/ttyUSB0'

Fix: Add your user to the dialout group:

sudo usermod -aG dialout $USER
# Log out and back in for the change to take effect

Or set permissions directly (temporary):

sudo chmod 666 /dev/ttyUSB0

MQTT connection refused

Symptom: ConnectionRefusedError or timeout when connecting to broker.

Checklist:

  1. Verify the broker is running: systemctl status mosquitto
  2. Check VITO2MQTT_MQTT__HOST is correct (hostname/IP, not URL)
  3. Check VITO2MQTT_MQTT__PORT matches the broker's listener port
  4. If using authentication, verify VITO2MQTT_MQTT__USERNAME and VITO2MQTT_MQTT__PASSWORD
  5. Check firewall rules allow connections on the MQTT port

P300 protocol timeout

Symptom: Timeout errors during Optolink communication.

Possible causes:

  • Wrong baud rate — The Vitodens 200-W uses 4800 baud (default). Ensure VITO2MQTT_SERIAL_BAUD_RATE=4800.
  • Adapter not connected — Verify the Optolink adapter is seated correctly on the boiler's service port.
  • USB adapter issue — Try unplugging and reconnecting the USB adapter. Check dmesg | tail for USB device errors.
  • Multiple readers — Only one application can use the serial port at a time. Ensure no other Optolink software is running.

No data on MQTT topics

Symptom: Subscribed to topics but no messages arrive.

Checklist:

  1. Verify vito2mqtt is running and connected (check log output)
  2. Confirm topic structure: vito2mqtt/vitodens200w/+/state
  3. Check VITO2MQTT_DEVICE_ID matches what you're subscribing to (default: vitodens200w)
  4. Try dry-run mode to isolate serial vs. MQTT issues: uv run vito2mqtt --dry-run