Skip to content

Instantly share code, notes, and snippets.

@lewurm
Last active July 2, 2025 18:08
Show Gist options
  • Save lewurm/6ab5a914209e8a16f4532ccfcd25b865 to your computer and use it in GitHub Desktop.
Save lewurm/6ab5a914209e8a16f4532ccfcd25b865 to your computer and use it in GitHub Desktop.
HomeAssistant Modbus configuration for Pichler LG350 Ventilation Units (possible for LG450 too)
modbus:
- name: pichlerlg350
type: tcp
host: 192.168.44.232
port: 502
delay: 2
message_wait_milliseconds: 30
binary_sensors:
- name: "lg350_bypassklappe" # 0.0
unique_id: "lg350_bypassklappe"
slave: 20
address: 20
input_type: input
device_class: opening
switches:
- name: "lg350_betriebsmodus_sommer"
slave: 20
address: 1
write_type: holding
command_on: 1 # Sommer
command_off: 2 # Winter
verify:
delay: 1
- name: "lg350_bypassklappe_force_open"
slave: 20
address: 49
write_type: holding
# data_type: uint16
command_on: 2 # Ein/Offen
command_off: 0 # Normal. "1" waere Aus/Geschlossen
verify:
delay: 1
sensors:
- name: "lg350_temp_aussenluft" # 0.0
unique_id: "lg350_temp_aussenluft"
unit_of_measurement: "°C"
slave: 20
address: 30
input_type: input
data_type: int16
offset: -100
precision: 1
scale: 0.1
- name: "lg350_temp_fortluft" # 3.0
unique_id: "lg350_temp_fortluft"
unit_of_measurement: "°C"
slave: 20
address: 31 # 30
input_type: input
data_type: int16
offset: -100
precision: 1
scale: 0.1
- name: "lg350_temp_abluft" # 0.2
unique_id: "lg350_temp_abluft"
unit_of_measurement: "°C"
slave: 20
address: 32 # 31
input_type: input
data_type: int16
offset: -100
precision: 1
scale: 0.1
- name: "lg350_temp_zuluft" # xxx
unique_id: "lg350_temp_zuluft"
unit_of_measurement: "°C"
slave: 20
address: 33 # 32
input_type: input
data_type: int16
offset: -100
precision: 1
scale: 0.1
- name: "lg350_temp_aussenluft_vhr"
unique_id: "lg350_temp_aussenluft_vhr"
unit_of_measurement: "°C"
slave: 20
address: 34
input_type: input
data_type: int16
offset: -100
precision: 1
scale: 0.1
- name: "lg350_zuluftdrehzahl"
unique_id: "lg350_zuluftdrehzahl"
unit_of_measurement: "rpm"
slave: 20
address: 39
input_type: input
data_type: int16
- name: "lg350_abluftdrehzahl"
unique_id: "lg350_abluftdrehzahl"
unit_of_measurement: "rpm"
slave: 20
address: 40
input_type: input
data_type: int16
- name: "lg350_zuluftstrom"
unique_id: "lg350_zuluftstrom"
unit_of_measurement: "m3/h"
slave: 20
address: 46
input_type: input
data_type: int16
- name: "lg350_abluftstrom"
unique_id: "lg350_abluftstrom"
unit_of_measurement: "m3/h"
slave: 20
address: 47
input_type: input
data_type: int16
- name: "lg350_reststandzeit_filter"
unique_id: "lg350_reststandzeit_filter"
unit_of_measurement: "h"
slave: 20
address: 50
input_type: input
data_type: uint16
- name: "lg350_betriebsmodus_sw"
unique_id: "lg350_betriebsmodus_sw"
slave: 20
address: 1
input_type: holding
data_type: uint16
- name: "lg350_lufstrom_stufe1"
unique_id: "lg350_luftstrom_stufe1"
unit_of_measurement: "m3/h"
slave: 20
address: 9
input_type: holding
data_type: int16
- name: "lg350_lufstrom_stufe2"
unique_id: "lg350_luftstrom_stufe2"
unit_of_measurement: "m3/h"
slave: 20
address: 10
input_type: holding
data_type: int16
- name: "lg350_lufstrom_stufe3"
unique_id: "lg350_luftstrom_stufe3"
unit_of_measurement: "m3/h"
slave: 20
address: 11
input_type: holding
data_type: int16
- name: "lg350_luftstufe"
unique_id: "lg350_luftstufe"
slave: 20
address: 2
input_type: holding
data_type: int16
- name: "lg350_bypassklappe_set"
unique_id: "lg350_bypassklappe_set"
slave: 20
address: 49
input_type: holding
data_type: int16
- name: "lg350_abluftdifferenz_stufe1"
unique_id: "lg350_abluftdifferenz_stufe1"
unit_of_measurement: "m3/h"
slave: 20
address: 83
input_type: holding
data_type: int16
offset: -500
- name: "lg350_abluftdifferenz_stufe2"
unique_id: "lg350_abluftdifferenz_stufe2"
unit_of_measurement: "m3/h"
slave: 20
address: 84
input_type: holding
data_type: int16
offset: -500
- name: "lg350_abluftdifferenz_stufe3"
unique_id: "lg350_abluftdifferenz_stufe3"
unit_of_measurement: "m3/h"
slave: 20
address: 85
input_type: holding
data_type: int16
offset: -500
sensor:
- platform: template
sensors:
lg350_luftstufe_render:
friendly_name: "Luftstufe"
value_template: >-
{% set lg350_luftstufe_mapper = {
0: 'Standby',
1: 'Stufe 1',
2: 'Stufe 2',
3: 'Stufe 3',
4: 'Grundlüftung'} %}
{% set state = states('sensor.lg350_luftstufe')|int %}
{{ lg350_luftstufe_mapper[state] if state in lg350_luftstufe_mapper else 'Unknown' }}
lg350_bypassklappe_set_render:
friendly_name: "Bypass Klappe Stellung"
value_template: >-
{% set lg350_bypassklappe_set_mapper = {
0: 'Normal',
1: 'Aus/Geschlossen',
2: 'Ein/Offen'} %}
{% set state = states('sensor.lg350_bypassklappe_set')|int %}
{{ lg350_bypassklappe_set_mapper[state] if state in lg350_bypassklappe_set_mapper else 'Unknown' }}
lg350_betriebsmodus_sw_render:
friendly_name: "Betriebsmodus"
value_template: >-
{% set lg350_betriebsmodus_sw_mapper = {
1: 'Sommer',
2: 'Winter'} %}
{% set state = states('sensor.lg350_betriebsmodus_sw')|int %}
{{ lg350_betriebsmodus_sw_mapper[state] if state in lg350_betriebsmodus_sw_mapper else 'Unknown' }}
template:
- sensor:
- name: "LG350 Heizleistung"
unique_id: "lg350_heizleistung"
state_class: "measurement"
unit_of_measurement: "W"
device_class: power
state: >
{% set vorlauf = states('sensor.lg350_temp_zuluft') | float (0) %}
{% set ruecklauf = states('sensor.lg350_temp_abluft') | float %}
{% set durchfluss = states('sensor.lg350_zuluftstrom') | float %}
{{ ((vorlauf - ruecklauf) * durchfluss * 0.33 ) | round(1, default=0) }}
@lewurm
Copy link
Author

lewurm commented Nov 5, 2024

How did you find the formula to calculate the heating power: (vorlauf - ruecklauf) * durchfluss * 0.33 ?

I assumed that 0.33Wh is needed to increase 1 m3 of air by 1 K. But indeed, it ignores humidity.

@lewurm
Copy link
Author

lewurm commented Nov 5, 2024

The manufacturer's documentation is publicly available:
https://www.pichlerluft.at/ -> Komfort-Service -> Download -> Unterlagen -> Modbuslisten LG-Serie

I swear this wasn't there a couple months ago 😅 thank you!!

@mihu-ov
Copy link

mihu-ov commented Nov 6, 2024

Forgive my ignorance, but can I save this as lg350.yml and just have an include in configuration.yaml? Or do I have to distribute this to modbus / sensor / template?
Thank you, just received my modbus interface and this helps a lot :-)

@lewurm
Copy link
Author

lewurm commented Nov 7, 2024

@mihu-ov you can copy paste it into your configuration.yaml or you try, as you suggested, to include it. Under Settings -> Devices & Services you should see a ModBus integration then with the entities defined.

FWIW I didn't use the Pichler Modbus device, but just attached it an ESP32 with an RS485 converter to "L11" (see picture). The ESP32 runs Tasmota and uses the the ModBus Bridge feature which makes it available as ModBus TCP, which can be accessed from Home Assistant then.

Screenshot 2024-11-07 at 15 19 53

@mihu-ov
Copy link

mihu-ov commented Nov 7, 2024

Thank you, working fine now with a cheap Elfin EE11A Ethernet to RS485 device connected to L11!

@lewurm
Copy link
Author

lewurm commented Nov 7, 2024

Thank you, working fine now with a cheap Elfin EE11A Ethernet to RS485 device connected to L11!

Nice! 🙂

@slkreuzer
Copy link

Hi @lewurm, hi @mihu-ov, I was following your discussion around the lg350 and using a Tasmota ModbusBridge via ESP32 +rs485 converter. I have completed the setup, ESP32 with RS485 module (the supported by Tasmota one), flashed with recent default tasmota32.bin, but when i run ModbusTCPStart 502 i get the unknown command error from Tasmota... Hooked it up to my lg350, same result. I guess i am missing some steps? Which ModBus serial settings did you use? Could you please point me in the right direction / provide a bit more detail on how to set up / configure Tasmota w. ModbusBridge-TCP? Thanks in advance!

@mihu-ov
Copy link

mihu-ov commented May 26, 2025

Sorry, I have no experience with Tasmota.

@lewurm
Copy link
Author

lewurm commented May 26, 2025

flashed with recent default tasmota32.bin, but when i run ModbusTCPStart 502 i get the unknown command error from Tasmota

Sounds like Modbus TCP support (USE_MODBUS_BRIDGE_TCP) is not included in your image, despite the documentation claiming that should be the case for tasmota32.bin. Does e.g. ModbusSerialConfig work? That would mean that at least USE_MODBUS_BRIDGE is enabled in your image. Which ESP32 are you using? Which Tasmota version are you using?

FWIW I'm using 13.2.0(tasmota32) on a XIAO ESP32C3.

@sventiffe
Copy link

sventiffe commented Jun 16, 2025

I'd like to add more sensors to my configuration. I do have a LG 150 but so far the configuration for 450 seems to work. There are some inconsistencies though (for example, the status of the bypass is "bypass" according to the touch panel, but "off" according to the modus signal). I have two newbie questions that you might be able to answer:

  • reading the data sheet, some addresses are assigned twice: for example, address 10 is supposed to be the datapoint for "T9_TouchDisplay" but also the setpoint "010. Luftstufe2". What am I missing? I'd like to be able to read both
    • edit: learned about "input_type" ie setting it to "holding" or "input" will do the trick
  • how does one snoop the modbus traffic on Home Assistant (running on Raspi)? I was hoping that the modbus integration would come with the option to create a dump but it seems that this is not the case

So i've added a bunch of sensors and will test the configuration with my LG 150. I still do not understand the discrepancy between the bypass signals on the touch display and in home assistant though. The other values seem to be reasonable.

Can anyone help me with understanding how to either

  • set the ventilation volume for one specific level or
  • set the ventilation level (essentially, writing to lg150_luftstufe address 2 holding, as opposed to just reading it)?

The latter would allow to manually control or to automate the ventilation control from home assistant.

Edit again, answering my question with https://www.energiesparhaus.at/forum-erfahrungsaustausch-pichlerluft-kompaktlueftungsgeraete-lg-350-lg-450/75919_6#831043

@lewurm
Copy link
Author

lewurm commented Jun 17, 2025

I answered (mostly) in the energiesparhaus thread.

set the ventilation volume for one specific level or

I configured an input_number for that in Home Assistant (Settings -> Devices & services -> Helpers), so that I have a nice UI slider.

Screenshot 2025-06-17 at 22 14 31

and then there is an automation associated with it:

alias: "lg350: base change "
description: ""
mode: single
triggers:
  - entity_id:
      - input_number.lg350_base_luftmenge
    from: null
    to: null
    trigger: state
conditions: []
actions:
  - data_template:
      hub: pichlerlg350
      address: 9
      slave: 20
      value: "{{ states('input_number.lg350_base_luftmenge') | int }}"
    action: modbus.write_register

You could do something similar for the ventilation level. Personally I just stick to one ventilation level and adapt the volume of that. For example, I've a CO2 sensor in my bedroom and control the ventilation level based on the CO2 concentration; that automation just changes input_number.lg350_base_luftmenge which will trigger the automation above to propagate the value to the correct modbus register.

@sventiffe
Copy link

sventiffe commented Jun 22, 2025

Thank you! As mentioned in the forum, I've received some (incomplete) documentation from Pichler for the LG 150 and updated my config here: https://gist.github.com/sventiffe/788b25ff50a8b3cc2c3a33cac5576616

I've defined an input number and action like suggested:

alias: 'lg150: luftmenge stufe 1 änderung'
description: ''
triggers:
  - entity_id:
      - input_number.lg150_base_luftmenge
    from: null
    to: null
    trigger: state
conditions: []
actions:
  - data_template:
      hub: pichler_lg150
      address: 9
      slave: 20
      value: '{{ states(''input_number.lg150_base_luftmenge'') | int / 10 }}'
    action: modbus.write_register
mode: single

In contrast, apparently, to the bigger devices, the LG150 operates with 1/10th of the actual settings. I've also defined helper methods to render the air volume nicely, multiplying the modbus data by 10

@silenthunter99
Copy link

Hello guys,
excuse me if it is a dumb question, First time doing something with Modbus RTU-> TCP and not too long in HA

Has anyone an idea why i can´t set any settings at the ventilation unit?
I have the same device Elfin EE11A as [mihu-ov].

Can´t seem to find the right setting for this

Elfin1 Elfin2

Elfin3`

Elfin seems to communicate with the HA instance

HA:
HA1

HA2 Sensor readings work

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment