How to parse bitfield from Modbus register

Hi,

I’ve been trying to setup my Enervent LTR-5 Z with an eAir controller via Modbus TCP in Home Assistant. I’ve managed to add all the sensors and basic temperature settings successfully, see configs below.

I am however stuck at parsing the bitfields. I’ve gotten to the point where I am able to parse the status register as an unsigned short integer, but I suspect that this is not the correct approach. The documentation states;

Bit 0 indicates Max cooling mode, bit 1: max heating. Bit 2: Machine is stopped due to A alarm. Bit 3 indicates the machine has been stopped by request (ie. not due to alarm condition). Bit 4: indicates Away state. Bit 5 is reserved. Bit 6 indicates temperature boosting, bit 7 CO2 boosting, bit 8 RH boosting, bit 9 manual boosting. Bit 10 overpressure mode, bit 11 cooker hood mode, bit 12 central vacuum cleaner mode. Bit 13 indicates cool-off period of electrical heating coil. Bit 14 indicates summer night cooling mode. Bit 15 indicates heat recovery wheel defrosting mode.  Value 0 indicates “normal” state, no special status is active. 

How can I split the bitfield in Home Assistant? My initial thought was just to convert it into a integer, and use a template to map the numbers to a sensible device status, but is there a better way?

The documentation for the modbus can be found here: Enervent eAir Modbus registers

configuration.yaml

modbus:
  - name: enervent
    type: tcp
    host: 10.0.90.182
    port: 502
    sensors: !include enervent-sensors.yaml
    switches: !include enervent-switches.yaml
    climates: !include enervent-climates.yaml

enervent-sensors.yaml

# Enervent LTR5 over Modbus
- name: "LTR5Z Supply Air Fan Speed"
  unit_of_measurement: "%"
  address: 3
  data_type: uint16

- name: "LTR5Z Extract Air Fan Speed"
  unit_of_measurement: "%"
  address: 4
  data_type: uint16

- name: "LTR5Z Outside Air Temperature"
  unit_of_measurement: °C
  address: 6
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Supply Air Temperature after HRC"
  unit_of_measurement: °C
  address: 7
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Supply Air Temperature after HRC and heating"
  unit_of_measurement: °C
  address: 8
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Waste Air Temperature"
  unit_of_measurement: °C
  address: 9
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Extract Air Temperature"
  unit_of_measurement: °C
  address: 10
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Extract Air Temperature After HRC"
  unit_of_measurement: °C
  address: 11
  scale: 0.1
  data_type: int16
  precision: 2
  device_class: temperature

- name: "LTR5Z Days until service reminder"
  unit_of_measurement: "day(s)"
  address: 538
  data_type: uint16

- name: "LTR5Z Status"
  address: 44
  data_type: custom
  input_type: holding
  count: 1
  structure: "<H"

- name: "LTR5Z Supply Air Efficiency"
  unit_of_measurement: "%"
  address: 29
  data_type: uint16

- name: "LTR5Z Exhaust Air Efficiency"
  unit_of_measurement: "%"
  address: 30
  data_type: uint16

- name: "LTR5Z Exhaust Air Relative Humidity"
  unit_of_measurement: "%"
  address: 13
  data_type: uint16
  device_class: humidity

- name: "LTR5Z Alarm 1 Type"
  address: 385
  data_type: uint16

- name: "LTR5Z Alarm 1 State"
  address: 386
  data_type: uint16

- name: "LTR5Z Alarm 1 Year"
  address: 387
  data_type: uint16

- name: "LTR5Z Alarm 1 Month"
  address: 388
  data_type: uint16

- name: "LTR5Z Alarm 1 Day"
  address: 389
  data_type: uint16

- name: "LTR5Z Alarm 1 Minute"
  address: 390
  data_type: uint16

- name: "LTR5Z Alarm 1 Minute"
  address: 391
  data_type: uint16

enervent-switches.yaml

- name: "LTR5Z Manual boost"
  address: 10
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Summer Night Cooling"
  address: 12
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Silent Mode"
  address: 47
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Away Mode"
  address: 1
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Long Away mode"
  address: 2
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Stop System"
  address: 0
  write_type: coil
  verify:
    state_on: 1
    state_off: 0

- name: "LTR5Z Alarm 1 State Acknowledged"
  address: 386
  verify:
    state_on: 0
    state_off: 1

enervent-climates.yaml

- name: "LTR5Z Temperature"
  address: 8
  data_type: int16
  max_temp: 40
  min_temp: 5
  target_temp_register: 135
  scale: 0.1
  temp_step: 1
  temperature_unit: C
1 Like

This is very interesting. I have enervent pingvin eair, and i am interested on this. Modbus, what is it and how did you connect it to enervent ?

According to the template documentation, there’s a bitwise_and filter that should be of use:

1 Like

I just had an Enervent Salla with eAir controller installed, so this is super useful for me too.

Modbus integration in Home Assistant can be activated by adding the device specific lines to your configuration.yaml.
So, basically copy-paste that configuration.yaml bit that bkvamme shared above to your HA config.yaml, but do remember to change the host address to match your Enervent Pingvin’s IP.
Then create the other configuration files that bkvamme shared in the same folder.
Test the modified configuration (in Developer Tools / YAML and, if everything was ok, Restart Home Assistant.
You should now have the sensors and switches that are defined in the new config files.

I did exactly that (and also changed LTR5Z to Enervent from the config files), and now I can see those sensors and switches. The temperature readings match those that the eAir panel displays and, for example, turning “Silent mode” on from Home Assistant, I can see the status change also on the eAir panel.

Thanks to bkvamme for saving me the trouble of figuring out how to configure this stuff on Home Assistant! :handshake:

Obviously i tried that allready :slight_smile: Im just getting connection errors on my eAir :confused:

2022-11-07 19:50:59.721 ERROR (SyncWorker_1) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused
2022-11-07 19:50:59.880 ERROR (SyncWorker_4) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused
2022-11-07 19:50:59.965 ERROR (SyncWorker_3) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused
2022-11-07 19:51:00.121 ERROR (SyncWorker_2) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused
2022-11-07 19:51:00.209 ERROR (SyncWorker_6) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused
2022-11-07 19:51:00.355 ERROR (SyncWorker_6) [pymodbus.client.sync] Connection to (192.168.1.15, 502) failed: [Errno 111] Connection refused

Anyways, it is connected to internet, it has ip and so on, i can change settings via my.enervent.com

Ok, this might be another silly question, but have you enabled modbus from the eAir system settings?

Not a silly question at all, i did not find any switch where to enable/disable it ? Just found a settings for it, and they all were for serial connection (1,19200, n) ?

EDIT: Ok, it seems that i have too old software version in my eAir:/

Ok, on my side it shows “eAir SW version: 2.14”.
Did you manage to get it upgraded?

If you go to: Main menu > Settings > System configuration
Do you now have Modbus settings at the end of the list? (second last item)
Under Modbus settings, the last setting is Modbus TCP. I think this settings was “off” by default.

Yeah i do not have that option at all. I contacted enervent and they said it needs software updates.

Both, eair and motherboard updates they said.

MD SW version 1.27
MD HW version C 1.02

Hmm, bummer!
Did they specify how those updates can be done? I mean, I don’t see any option to trigger that from either the eAir controller or through my.enervent.com.

It is done with usb Stick. I presume they will do it themself, price, i dont know… After i asked is it free, they have not contacted back :smiley:

Edit: i just called them, they will send usb Stick and sdcard for the controller :slight_smile: for free.

Have you guys checked the physicall state of the eAir controller battery ? After 3years, mine is allready swollen a bit, so i took it out. Works fine without, in its gradle of course.
I heard that this is common problem and some have had burning controllers etc…

Just checked the one on mine: no signs of swelling.
But mine is brand new anyway. :man_shrugging:t3:

1 Like

To continue on the original topic, I finally managed to get those bitfield modbus registers into use, based on the tip from @Edwin_D:

First I changed the sensor to use big-endian byte order, to better match what the Enervent modbus registers are handing out:

- name: "Enervent Status"
  address: 44
  data_type: custom
  input_type: holding
  count: 1
  structure: ">H"

Then I created a template sensor with attributes for each mode, using the bitwise_and filter:

- sensor:
  - name: "Enervent status map"
    state: "{{ states('sensor.enervent_status') }}"
    attributes:
      max_cooling: "{% if states('sensor.enervent_status') | int | bitwise_and(1) %}on{% else %}off{% endif %}"
      max_heating: "{% if states('sensor.enervent_status') | int | bitwise_and(2) %}on{% else %}off{% endif %}"
      stopped_due_to_alarm: "{% if states('sensor.enervent_status') | int | bitwise_and(4) %}on{% else %}off{% endif %}"
      stopped_by_request: "{% if states('sensor.enervent_status') | int | bitwise_and(8) %}on{% else %}off{% endif %}"
      away_mode: "{% if states('sensor.enervent_status') | int | bitwise_and(16) %}on{% else %}off{% endif %}"
      temp_boosting: "{% if states('sensor.enervent_status') | int | bitwise_and(64) %}on{% else %}off{% endif %}"
      co2_boosting: "{% if states('sensor.enervent_status') | int | bitwise_and(128) %}on{% else %}off{% endif %}"
      rh_boosting: "{% if states('sensor.enervent_status') | int | bitwise_and(256) %}on{% else %}off{% endif %}"
      manual_boosting_mode: "{% if states('sensor.enervent_status') | int | bitwise_and(512) %}on{% else %}off{% endif %}"
      overpressure_mode: "{% if states('sensor.enervent_status') | int | bitwise_and(1024) %}on{% else %}off{% endif %}"
      cooker_hood_mode: "{% if states('sensor.enervent_status') | int | bitwise_and(2048) %}on{% else %}off{% endif %}"

I’ve only tested Away mode, Manual boosting & overpressure modes, as those can be directly enabled from the panel. But given the bit order, the other states should work just the same.

Based on the verified logic it’s then easy to create template sensors for individual modes.
For example:

- binary_sensor:
  - name: "Enervent away mode"
    state: "{% if states('sensor.enervent_status') | int | bitwise_and(16) %}on{% else %}off{% endif %}"
    icon: mdi:home-export-outline

I hope this helps someone who’s trying to integrate their Enervent ventilator to Home Assistant! :slightly_smiling_face:

[edit: fixed a copy-paste mistake ]

4 Likes

Hmm… for some reason my Enervent modbus connection dies after a day or so. Reloading modbus from developer tools does not help, only restarting homeassistant works ?

2022-11-30 09:21:52.760 ERROR (SyncWorker_8) [homeassistant.components.modbus.modbus] Pymodbus: enervent: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)

I haven’t had much luck with reloading modbus yaml through Developer Tools either. Seems like you need to restart Home Assistant every time you make some changes to the config.
Haven’t had any disconnection issues on my end but, with a previous version of Home Assistant the connection was lost when I tried to reload modbus yaml.

Have you been able to add those preset modes (away etc.) to climate cards ? If so, how ?

Not exactly.
I’m using a vertical stack to group all things related to the Enervent ventilator. I have button cards mapped to those switches (modbus coil registers) for the preset modes I expect to be using.
Here’s a screen shot.

2 Likes

Yeah, i gave up and configured my own with mushroom cards…

kuva

1 Like

I recently change to HASS OS, and there restart via developer tools work. I Also learned that there is service to restart modbus connection. I was wondering if its possible to make automation to check whether connection is up or not, and use that modbus restart service. I just dont know how to check if connection is up…