Controlling Viessmann Vitodens Gas Heater Boilers locally (new models 100-W, 200-W, ... after 2018)

Yes, I can switch all modes, also Standby/DHW/DHW & Heating modes as well. Which is not available in HA climate entity but has to be controlled by additional select entity (partially mapped to standard climate entity).

Pay attention to the number of parameters you monitor in HA - too many will require infrequent updates as the optolink interface is not very fast.

Here is my up to date config:

1 Like

Thanks for such a prompt reply.
As I have in hand Wemos D1 (ESP8266) and I’m new to esphome, do I need to change in esp config something more than rx/tx pins assignment?
HA config - will it be imported upon configuring esp device?

I recommend to go with esp32 as 8266 has limited resources, unless you just want to try it with only a few sensors.

HA will automatically import it

Thanks for clarification!

Hi again @adorobis
Question about commented out sections in your config. Are those working configs (exhaust or return temperatures) but not needed in your use case?
And also I don’t see below code as well:

text_sensor:
  - platform: optolink
    name: Error history 1
    address: 0x7590
    bytes: 9
    type: RAW
  - platform: optolink
    name: Heating schedule Monday
    type: DAY_SCHEDULE
    day_of_week: MONDAY
    address: 0x2000
    bytes: 56

It’s missing to reduce number of datapoints?

No, in my case they are duplicating info from other sensors.

This one works ok, I keep only last two errors - good enough for me. But I configured it with mapping to error description:

  - platform: optolink
    name: Error code 1
    address: 0x7507
    bytes: 1
    update_interval: 591s
    icon: mdi:note-text-outline
    type: MAP
    filters:
      - map:
        - "0 -> regular operation"
        - "15 -> 0 F : Przeprowadzić konserwację. Po konserwacji ustawić kodowanie 24:0"
        - "16 -> 1 0 : Zwarcie czujnika temperatury zewnętrznej"
        - "231 -> E 7 : Usterka palnika"
        - "232 -> E 8 : Usterka palnika"
        - "238 -> E E : Blokada palnika"

I don’t use this one. I have not really found good use to replicate it in HA.

Hi,

I have configured esp32-s3.
My config is as follows:

substitutions:
  device_name: viessmann
  device_description: "Viessmann Optolink"
  update_interval_global: 60s
  update_interval_short: 15s
  update_interval_long: 600s

esphome:
  name: '${device_name}'
  friendly_name: Viessmann
  project:
    name: esphome.viessmann
    version: "1.0"

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino
  flash_size: 16MB

# Enable logging
logger:
  hardware_uart: UART0
  baud_rate: 0
  level: debug

# Enable Home Assistant API
api:
  encryption:
    key: "xxx"

ota:
  - platform: esphome
    password: "xxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Viessmann Fallback Hotspot"
    password: "xxx"

captive_portal:

dashboard_import:
  package_import_url: github://esphome/firmware/esphome-web/esp32s3.yaml@main
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:

esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true

bluetooth_proxy:
  active: true

external_components:
  - source: github://pr#4453
    components: [ optolink ]

optolink:
  protocol: KW
#  device_info: Device Info
#  state: Component state
  rx_pin: 44
  tx_pin: 43

button:
  - platform: restart
    name: "Restart"

text_sensor:
  - platform: version
    hide_timestamp: true
    name: "ESPHome Version"

  - platform: wifi_info
    ip_address:
      name: "IP Address"
      icon: mdi:wifi
    ssid:
      name: "Connected SSID"
      icon: mdi:wifi-strength-2

  - platform: optolink
    type: DEVICE_INFO
    name: Device Info
    update_interval: 969s
  - platform: optolink
    type: STATE_INFO
    name: Component state
    update_interval: 459s
    
  - platform: optolink
    name: Current Operating Mode A1
    address: 0x2500
    bytes: 2
    update_interval: 128s
    type: MAP
    filters:
      - map:
        - "2 -> off"           # when in standby or dhw modes
        - "258 -> reduced"     # when in reduced (night) or forcedReduced modes or in Eco preset
        - "514 -> comfort"     # when in day heating or in Comfort preset
        - "770 -> normal"      # when in forcedNormal type
  - platform: optolink
    name: Current Operating Mode M2
    address: 0x3500
    bytes: 2
    update_interval: 130s
    type: MAP
    filters:
      - map:
        - "2 -> off"
        - "258 -> reduced"
        - "514 -> comfort"
        - "770 -> normal"

  # - platform: optolink
  #   name: Error history 1
  #   address: 0x7590
  #   bytes: 9
  #   type: RAW
 # above disabled for last two errors configured with mapping to error description
 # errors : 0:7507, 1:7510, 2:7519, 3:7522, 4:752B, 5:7534, 6:753D, 7:7546, 8:754F, 9:7558
  - platform: optolink
    name: Error code 1
    address: 0x7507
    bytes: 1
    update_interval: 591s
    icon: mdi:note-text-outline
    type: MAP
    filters:
      - map:
        - "0 -> regular operation"
        - "15 -> 0 F : Przeprowadzić konserwację. Po konserwacji ustawić kodowanie 24:0"
        - "16 -> 1 0 : Zwarcie czujnika temperatury zewnętrznej"
        - "231 -> E 7 : Usterka palnika"
        - "232 -> E 8 : Usterka palnika"
        - "238 -> E E : Blokada palnika"
  - platform: optolink
    name: Error code 2
    address: 0x7510
    bytes: 1
    update_interval: 589s
    icon: mdi:note-text-outline
    type: MAP
    filters:
      - map:
        - "0 -> regular operation"
        - "15 -> 0 F : Przeprowadzić konserwację. Po konserwacji ustawić kodowanie 24:0"
        - "16 -> 1 0 : Zwarcie czujnika temperatury zewnętrznej"
        - "231 -> E 7 : Usterka palnika"
        - "232 -> E 8 : Usterka palnika"
        - "238 -> E E : Blokada palnika"
#  - platform: optolink
#    name: Error code 3
#    address: 0x7519
#    bytes: 1
#    update_interval: 587s
#    icon: mdi:note-text-outline
#    filters:
#      - map:
#        - "0 -> regular operation"
#        - "15 -> 0 F : Przeprowadzić konserwację. Po konserwacji ustawić kodowanie 24:0"
#        - "16 -> 1 0 : Zwarcie czujnika temperatury zewnętrznej"
#        - "231 -> E 7 : Usterka palnika"
#        - "232 -> E 8 : Usterka palnika"
#        - "238 -> E E : Blokada palnika"
      
binary_sensor:
  - platform: optolink
    name: Burner
    address: 0x55D3
    update_interval: 52s
    device_class: heat
    
  - platform: optolink
    name: DHW Charging
    address: 0x650A
    update_interval: 50s
    device_class: heat

#  - platform: optolink
#    name: Storage Charging Pump
#    address: 0x6513
#    update_interval: 52s
#    device_class: power
    
  - platform: optolink
    name: Circulation Pump
    address: 0x6515
    update_interval: 54s
    device_class: power

  - platform: optolink
    name: Heating circuit pump A1M1
    address: 0x2906
    update_interval: 56s
    device_class: power

  - platform: optolink
    name: Heating circuit pump M2
    address: 0x3906
    update_interval: 64s
    device_class: power

  - platform: optolink
    name: Frost protection
    address: 0x27A4
    update_interval: 593s
    device_class: cold

number:
#  - platform: optolink
#    name: Time limit party and operating mode switching
#    address: 0x27F2
#    bytes: 1
#    min_value: 0
#    max_value: 100
#    step: 1
#    type: box
#    update_interval: 595s

  - platform: optolink
    name: Room Temperature Setpoint
    unit_of_measurement: °C
    address: 0x2306
    bytes: 1
    min_value: 18
    max_value: 27
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 315s

  - platform: optolink
    name: Reduced Room Temperature Setpoint
    unit_of_measurement: °C
    address: 0x2307
    bytes: 1
    min_value: 10
    max_value: 20
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 597s

  - platform: optolink
    name: Party Temperature Setpoint
    unit_of_measurement: °C
    address: 0x2308
    bytes: 1
    min_value: 18
    max_value: 27
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 599s

  - platform: optolink
    name: Floor Temperature Setpoint
    unit_of_measurement: °C
    address: 0x3306
    bytes: 1
    min_value: 18
    max_value: 27
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 280s

  - platform: optolink
    name: Reduced Floor Temperature Setpoint
    unit_of_measurement: °C
    address: 0x3307
    bytes: 1
    min_value: 10
    max_value: 20
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 591s

  - platform: optolink
    name: Party Floor Temperature Setpoint
    unit_of_measurement: °C
    address: 0x3308
    bytes: 1
    min_value: 18
    max_value: 27
    step: 1
    icon: "mdi:home-thermometer"
    device_class: temperature
    update_interval: 593s

  - platform: optolink
    name: Heating curve level
    address: 0x27D4
    bytes: 1
    min_value: 0
    max_value: 10
    step: 1
    mode: box
    update_interval: 601s

  - platform: optolink
    name: Heating curve slope
    address: 0x27D3
    bytes: 1
    min_value: 0
    max_value: 3
    step: 0.1
    mode: box
    div_ratio: 10
    update_interval: 603s
    
  - platform: optolink
    name: Floor Heating curve level
    address: 0x37D4
    bytes: 1
    min_value: 0
    max_value: 10
    step: 1
    mode: box
    update_interval: ${update_interval_long}

  - platform: optolink
    name: Floor Heating curve slope
    address: 0x37D3
    bytes: 1
    min_value: 0
    max_value: 3
    step: 0.1
    mode: box
    div_ratio: 10
    update_interval: 607s

  - platform: optolink
    name: DHW Temperature Setpoint
    unit_of_measurement: °C
    address: 0x6300
    bytes: 1
    min_value: 30
    max_value: 60
    step: 1
    icon: "mdi:water-boiler"
    device_class: temperature
    update_interval: 605s

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    id: '${device_name}_wifi_signal'
    update_interval: ${update_interval_global}

  - platform: optolink
    type: QUEUE_SIZE
    name: Optolink Queue Size
    icon: mdi:queue-first-in-last-out
    entity_category: diagnostic
    update_interval: 5s

  - platform: optolink
    name: Target flow temperature A1M1
    address: 0x2544
    bytes: 2
    unit_of_measurement: °C
    device_class: temperature
    div_ratio: 10
    accuracy_decimals: 1
    update_interval: 330s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Target flow temperature M2
    address: 0x3544
    bytes: 2
    unit_of_measurement: °C
    device_class: temperature
    div_ratio: 10
    accuracy_decimals: 1
    update_interval: 332s
    state_class: measurement
    force_update: true

# Enabled for test
  - platform: optolink
    name: Return temperature
    address: 0x0816
    bytes: 2
    div_ratio: 1
    filters:
      multiply: 0.1
    accuracy_decimals: 1
    unit_of_measurement: °C
    device_class: temperature
    update_interval: 62s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Flow temperature M2
    address: 0x3900
    bytes: 2
    div_ratio: 1
    filters:
      multiply: 0.1
    accuracy_decimals: 1
    unit_of_measurement: °C
    device_class: temperature
    update_interval: 48s
    state_class: measurement
    force_update: true

# Enabled for test
  - platform: optolink
    name: Exhaust temperature
    address: 0x0808
    bytes: 2
    div_ratio: 1
    filters:
      multiply: 0.1
    accuracy_decimals: 1
    unit_of_measurement: °C
    device_class: temperature
    update_interval: 68s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Storage temperature
    address: 0x0812
    bytes: 2
    div_ratio: 1
    filters:
      multiply: 0.1
    accuracy_decimals: 1
    unit_of_measurement: °C
    device_class: temperature
    update_interval: 175s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Outside Temperature
    address: 0x0800
    #address: 0x5527
    bytes: 2
    unit_of_measurement: °C
    device_class: temperature
    div_ratio: 10
    accuracy_decimals: 1
    update_interval: 611s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Boiler Temperature
    address: 0x0802
    bytes: 2
    div_ratio: 10
    accuracy_decimals: 1
    unit_of_measurement: °C
    device_class: temperature
    update_interval: 28s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Room temperature
    address: 0x0896
    bytes: 2
    div_ratio: 1
    unit_of_measurement: °C
    device_class: temperature
    accuracy_decimals: 1
    filters:
      multiply: 0.1
    update_interval: 607s
    state_class: measurement
    force_update: true

  - platform: optolink
    name: Operating Hours
    address: 0x08A7
    bytes: 4
    device_class: duration
    unit_of_measurement: h
    state_class: total_increasing
    icon: mdi:counter
    update_interval: 909s
    filters:
      - lambda: return x / 3600;
    accuracy_decimals: 1
    force_update: true

  - platform: optolink
    name: Burner Starts
    address: 0x088A
    bytes: 4
    update_interval: 911s
    state_class: total_increasing
    icon: mdi:counter
    force_update: true

  - platform: optolink
    name: Burner Performance
    address: 0xA38F
    bytes: 1
    filters:
      multiply: 0.5
    update_interval: ${update_interval_short}
    unit_of_measurement: "%"
    state_class: measurement
    icon: mdi:percent
    force_update: true

select:
  - platform: optolink
    name: Operation mode
    address: 0x2323
    bytes: 1
    map:
      - "0 -> standby"
      - "1 -> dhw"
      - "2 -> dhwAndHeating"
      - "3 -> forcedReduced"
      - "4 -> forcedNormal"
    update_interval: 68s
    
  - platform: optolink
    name: Operation mode M2
    address: 0x3323
    bytes: 1
    map:
      - "0 -> standby"
      - "1 -> dhw"
      - "2 -> dhwAndHeating"
      - "3 -> forcedReduced"
      - "4 -> forcedNormal"
    update_interval: 123s

  - platform: template
    name: "Preset"
    id: preset
    update_interval: 10s
    options:
      - "off"
      - "eco"
      - "comfort"
    set_action:
      - lambda: |-
          if (x == "off") {
            id(economy_mode).turn_off();
            id(party_mode).turn_off();
          } else if (x == "eco"){
            id(economy_mode).turn_on();
            id(party_mode).turn_off();
          } else if (x == "comfort"){
            id(economy_mode).turn_off();
            id(party_mode).turn_on();
          }          
    lambda: !lambda |-
      if (not(id(economy_mode).state) and not(id(party_mode).state)) {
        return (std::string) "off";
      } else if (id(economy_mode).state) {
          return (std::string) "eco";
      } else if (id(party_mode).state) {
          return (std::string) "comfort";
      } else {
        return (std::string) "off";
      }
      
  - platform: template
    name: "Preset M2"
    id: preset_m2
    update_interval: 10s
    options:
      - "off"
      - "eco"
      - "comfort"
    set_action:
      - lambda: |-
          if (x == "off") {
            id(economy_mode_m2).turn_off();
            id(party_mode_m2).turn_off();
          } else if (x == "eco"){
            id(economy_mode_m2).turn_on();
            id(party_mode_m2).turn_off();
          } else if (x == "comfort"){
            id(economy_mode_m2).turn_off();
            id(party_mode_m2).turn_on();
          }          
    lambda: !lambda |-
      if (not(id(economy_mode_m2).state) and not(id(party_mode_m2).state)) {
        return (std::string) "off";
      } else if (id(economy_mode_m2).state) {
          return (std::string) "eco";
      } else if (id(party_mode_m2).state) {
          return (std::string) "comfort";
      } else {
        return (std::string) "off";
      }
      
switch:
  - platform: optolink
    name: Economy mode
    id: economy_mode
    address: 0x2302
    icon: mdi:sprout-outline
    update_interval: 123s

  - platform: optolink
    name: Party mode
    id: party_mode
    address: 0x2303
    icon: mdi:glass-cocktail
    update_interval: 72s

  - platform: optolink
    name: Economy mode M2
    id: economy_mode_m2
    address: 0x3302
    icon: mdi:sprout-outline
    update_interval: 303s

  - platform: optolink
    name: Party mode M2
    id: party_mode_m2
    address: 0x3303
    icon: mdi:glass-cocktail
    update_interval: 307s

I’m not so sure about leaving BLE enabled but I don’t think it will make any harm?
Portal works as well:

Other thing is numbering of rx, tx pins. As per:


I set:

  rx_pin: 44
  tx_pin: 43

Is it correct?

But main question is: does optolink software contain any presets and may change settings of viessmann heater upon connecting this device?

Yes, that should be correct.

No, it does not send any config/parameter change commands upon connecting. You have to send them explicitly.

1 Like

Once again big thanks! And I’m in shock - first attempt and it works:

The only thing I’m not so sure of, are these errors:
errors

I don’t see them displayed on heater’s display plus temperature readings are ok and do correspond to other outdoor temperature sensors readings…
So, is mapping wrong or it is an “old” - inactive error?

EDIT:
Presets: Eco and comfort, what do they set and how can verify/edit those settings being changed?

Those could be old errors. Check the device manual - there is an option to go through the history of errors. It will also have mapping from the hex code to what it actually means.

Eco is switching the heater to the reduced temp. mode - it stays this way until next scheduled time
Comfort - the other way round - switches to normal temp mode until the next scheduled time.
You can see and control them on the room or heater controller - depending on which version you have

It is getting more clear, thanks again!
One, last question: “Optolink Queue Size” - it shows values between 70 and 80. Is it ok or it means update intervals issue?

EDIT:
Maybe one more question: When I switch party mode on in heater’s controller (locally) - it changes status of party mode switch (in HA) to “on” and preset to “comfort”.
Other way around, switching party mode on in HA, it toggles preset to “comfort” as well but I don’t see status changed in heater’s controller - party mode indicator LED remains unlit… Is it as expected?

That is most likely representing an issue. You have too many entities being updated too frequently.
This in my opinion also most likely impacts your attempt to control the party mode as requests are not getting processed or it takes long time before they are.
Try to increase the update interval so that the queue size does not exceed 10 at any time.
You can also increase update interval of some entities to very high values for entities that don’t change at all like the heating curve etc.

Hello, I also have the same model of boiler as Viessmann, but today I used an OT device to connect it and found that I could only read the data of the hot water and could not perform any control. I read the instructions and found that there is a jumper between L and 1 that needs to be remove, but I checked and found that my boiler does not have this jumper. Does your boiler have it? How did you connect it?

Other Viessmann boilers need to be set to OT mode in the menu after connecting to the OT device. Ours does not have a menu, do we need to set it?



Unfortunately, it seems like I’m not able to change/activate Party Mode via optolink. After toggling any of switches:

switch:
  - platform: optolink
    name: Economy mode
    id: economy_mode
    address: 0x2302
    icon: mdi:sprout-outline
    update_interval: 123s

  - platform: optolink
    name: Party mode
    id: party_mode
    address: 0x2303
    icon: mdi:glass-cocktail
    update_interval: 72s

  - platform: optolink
    name: Economy mode M2
    id: economy_mode_m2
    address: 0x3302
    icon: mdi:sprout-outline
    update_interval: 303s

  - platform: optolink
    name: Party mode M2
    id: party_mode_m2
    address: 0x3303
    icon: mdi:glass-cocktail
    update_interval: 307s

They change (temporarily) its own presets and after several seconds all goes back to previous state (I guess once values are read again).
There is a post: Viessmann OpenV vcontrold Client (Optolink) - #62 by pashdown saying that setting new values doesn’t work…

@adorobis does it work on your side? And if so, did you need to change anything else in boiler/heater?

Yes, party and eco modes work for my heater, even on both circuits (main and the floor heating). I did not have to change anything in config or in any other place as far as I remember. The only thing is that it takes sometimes even 2-3 minutes before the eco or party mode is really activated.

I know it’s a bit wild guess but I’ve just found page: OpenV_NodeMCU/VitoWifi_NodeMCU.ino at 12eca651bbfbc7485fad270e2ef1afbed0b84d6f · Schnup89/OpenV_NodeMCU · GitHub
with following code:

//###### Betriebsarten
DPMode getBetriebArtM1("getbetriebartm1","betriebsarten", 0x2301);     //HK1 0=Abschaltb,1=nur WW,2=heiz+WW, 3=DauernRed,3=Dauer Norma.
DPMode getBetriebArtM2("getbetriebartm2","betriebsarten", 0x3301);     //HK2 0=Abschaltb,1=nur WW,2=heiz+WW, 3=DauernRed,3=Dauer Norma.
DPStat getBetriebPartyM1("getbetriebpartym1","betriebsarten", 0x2303); //HK1 Party
DPStat getBetriebPartyM2("getbetriebpartym2","betriebsarten", 0x3303); //HK2 Party
DPStat setBetriebPartyM1("setbetriebpartym1","betriebsarten", 0x2330); //HK1 Party schreiben
DPStat setBetriebPartyM2("setbetriebpartym2","betriebsarten", 0x3330); //HK2 Party schreiben

Above shows that addresses configured in my setup:

0x2303
0x3303

are used to retrieve party mode status…
I can’t find list of optolink functions addresses, can anybody confirm them?

Those addresses are used both for reading and setting the state. And that’s how it is done in the optolink integration as well with the switch entities:

switch:
  - platform: optolink
    name: Economy mode
    id: economy_mode
    address: 0x2302
    icon: mdi:sprout-outline
    update_interval: 123s

  - platform: optolink
    name: Party mode
    id: party_mode
    address: 0x2303
    icon: mdi:glass-cocktail
    update_interval: 72s

Would have been safe enough to try for setting party mode these addresses:

0x2330
0x3330

instead of already in use:

0x2303
0x3303

?

Ok, If I change party mode switches’ addresses to:

0x2330
0x3330

they remain constantly ON.

when set as:

0x2303
0x3303

switches are OFF.

If I toggle them, they switch themselves back respectively, to their initial status.

On the other hand, Current Operating Mode A1 value is “773”. Nothing of configured mapping:

  - platform: optolink
    name: Current Operating Mode A1
    address: 0x2500
    bytes: 2
    update_interval: 128s
    type: MAP
    filters:
      - map:
        - "2 -> off"           # when in standby or dhw modes
        - "258 -> reduced"     # when in reduced (night) or forcedReduced modes or in Eco preset
        - "514 -> comfort"     # when in day heating or in Comfort preset
        - "770 -> normal"      # when in forcedNormal type

What is the meaning of “773” status? If I’m not mistaken, my hardware configuration is that M1 heats up boiler (water heat accumulator) with secondary loops’ coils inside.
And M2 is those secondary loops…
Current Operating Mode M2 keeps showing “comfort”

Thus, is there any way to get Party Mode switches functioning?