Integrate Poloplast PoloAir ventilation systems using Modbus integration

UPDATE 2021/09/23: New YAML for Modbus after change in syntax.
UPDATE 2021/11/30: Fix data types, add energy sensors compatible with long-term stats / energy dashboard

If you live in Western Europa (esp. the DACH region) you might have a Poloplast PoloAir ventilation system in your house, which can be fully integrated into Home Assistant using the existing Modbus integration. Here’s how it is working very well for me with a PoloAir 450+…

Prerequisites / Caveats:

  • PoloAir system needs to be part of your network using the ethernet interface, you know the IP address
  • The Modbus interface is open, no additional license or config necessary, BUT:
  • Note that the Modbus interface on the PoloAir is not secured in any way - meaning you can read and write to it without authentication of any kind. Depending on your network you might want to firewall it and only let your HAss instance communicate to it over the respectice port(s).
  • It is also possible to use a serial bus interface - haven’t tried it, should work the same, please refer to the Modbus integration documentation and the manual below.

You can find the whole Modbus config incl. all registers for the PoloAir >>here<<.

If you want to use the energy sensors created below in the HA Energy Dashboard add this to the corresponding parts of your configuration.yaml:

homeassistant:
  customize_glob:
    sensor.polo_energy*:
      last_reset: '1970-01-01T00:00:00+00:00'
      device_class: energy
      state_class: measurement

I assume that you have a separate Modbus YAML File - if not you have to preface this with modbus: and indent everything two spaces.

# Sensors for PoloAir 450+

- name: polo
  type: tcp
  host: 192.168.23.8
  port: 502
  retry_on_empty: true
  retries: 5
  sensors:
    - name: polo_OnOff
      slave: 1
      address: 0
      lazy_error_count: 2
      data_type: uint16
    - name: polo_EcoMode
      slave: 1
      address: 2
      lazy_error_count: 2
      data_type: uint16
    - name: polo_AutoMode
      slave: 1
      address: 3
      lazy_error_count: 2
      data_type: uint16
    - name: polo_CurrentMode
      slave: 1
      address: 4
      lazy_error_count: 2
      data_type: uint16
    - name: polo_NextMode
      slave: 1
      address: 6
      lazy_error_count: 2
      data_type: uint16
    - name: polo_ActiveAlarm
      slave: 1
      address: 599
      lazy_error_count: 2
      data_type: uint16
    - name: polo_FanAirIn
      slave: 1
      address: 909
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: '%'
    - name: polo_FanAirOut
      slave: 1
      address: 910
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: '%'
    - name: polo_TempAirOutside
      slave: 1
      address: 903
      lazy_error_count: 2
      data_type: int16
      unit_of_measurement: °C
      device_class: temperature
    - name: polo_TempAirIn
      slave: 1
      address: 901
      lazy_error_count: 2
      data_type: int16
      unit_of_measurement: °C
      device_class: temperature
    - name: polo_TempAirOut
      slave: 1
      address: 902
      lazy_error_count: 2
      data_type: int16
      unit_of_measurement: °C
      device_class: temperature
    - name: polo_FilterImpurity
      slave: 1
      address: 916
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: '%'
    - name: polo_Energy
      slave: 1
      address: 920
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: 'W'
    - name: polo_Heater
      slave: 1
      address: 921
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: 'W'
    - name: polo_HeatexEnergy
      slave: 1
      address: 922
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: 'W'
    - name: polo_HeatexEfficiency
      slave: 1
      address: 923
      lazy_error_count: 2
      data_type: uint16
      unit_of_measurement: '%'
    - name: polo_TempPanel
      slave: 1
      address: 945
      lazy_error_count: 2
      data_type: int16
      unit_of_measurement: °C
      device_class: temperature
    - name: polo_energy_day
      slave: 1
      address: 926
      lazy_error_count: 2
      data_type: uint32
      unit_of_measurement: 'kWh'
      scale: 0.001
      precision: 2
      state_class: measurement
    - name: polo_energy_month
      slave: 1
      address: 928
      lazy_error_count: 2
      data_type: uint32
      unit_of_measurement: 'kWh'
      scale: 0.001
      precision: 2
      state_class: measurement
    - name: polo_energy_total
      slave: 1
      address: 930
      lazy_error_count: 2
      data_type: uint32
      unit_of_measurement: 'kWh'
      scale: 0.001
      precision: 2
      state_class: measurement

In your sensors.yaml:

# Transforming sensors from raw data to sensible formats / modes
- platform: template
  sensors:
    tpolo_currentmode:
      friendly_name: "Aktueller Modus"
      entity_id: sensor.polo_currentmode
      value_template: >-
        {% set mapper =  {
            '0' : 'Standby',
            '1' : 'Abwesend',
            '2' : 'Normal',
            '3' : 'Intensiv',
            '4' : 'Boost',
            '5' : 'Küche',
            '6' : 'Feuer',
            '7' : 'Override',
            '8' : 'Urlaub',
            '9' : 'Luftqualität',
            '10' : 'Aus' } %}
        {% set state =  states.sensor.polo_currentmode.state %}
        {{ mapper[state] if state in mapper else 'Unknown' }}

- platform: template
  sensors:
    tpolo_nextmode:
      friendly_name: "Nächster Modus"
      entity_id: sensor.polo_nextmode
      value_template: >-
        {% set mapper =  {
            '0' : 'Standby',
            '1' : 'Abwesend',
            '2' : 'Normal',
            '3' : 'Intensiv',
            '4' : 'Boost',
            '5' : 'Küche',
            '6' : 'Feuer',
            '7' : 'Override',
            '8' : 'Urlaub' } %}
        {% set state =  states.sensor.polo_nextmode.state %}
        {{ mapper[state] if state in mapper else 'Unknown' }}

- platform: template
  sensors: 
    tpolo_tempairoutside:
      friendly_name: 'Außenlufttemperatur'
      device_class: temperature
      unit_of_measurement: '°C'
      value_template: '{{ states.sensor.polo_tempairoutside.state | float / 10 }}'
    tpolo_tempairin:
      friendly_name: 'Zulufttemperatur'
      device_class: temperature
      unit_of_measurement: '°C'
      value_template: '{{ states.sensor.polo_tempairin.state | float / 10 }}'
    tpolo_tempairout:
      friendly_name: 'Ablufttemperatur'
      device_class: temperature
      unit_of_measurement: '°C'
      value_template: '{{ states.sensor.polo_tempairout.state | float / 10 }}'
    tpolo_fanairin:
      friendly_name: 'Zuluftstrom'
      unit_of_measurement: '%'
      value_template: '{{ ((states.sensor.polo_fanairin.state | float / 10 )) | round(0) }}'
    tpolo_fanairout:
      friendly_name: 'Abluftstrom'
      unit_of_measurement: '%'
      value_template: '{{ ((states.sensor.polo_fanairout.state | float / 10 )) | round(0) }}'

If you need more / different sensors just refer to the documenation linked above and use the register.
Important: Register no. in HAss config = documenation - 1 (don’t know why…)

You can use this service call to change registers (refer to documentation):

    - service: modbus.write_register
      data:
        address: 3   # Register you want to adress
        unit: 1
        value: 1   # Value you want to write into the register
        hub: polo  

Some examples for automations.yaml:

# Ventilation

- alias: 'Activate summer mode'
  trigger:
    - platform: time
      at: '06:00:00'
  condition:
    - condition: numeric_state
      entity_id: sensor.weather_today_max_temp
      above: 27
  action:
    - service: modbus.write_register  #Active auto mode which is configured in PoloAir
      data:
        address: 3
        unit: 1
        value: 1
        hub: polo
    - delay: 00:00:10
    - service: modbus.write_register   #Deactivate Eco mode 
      data:
        address: 2
        unit: 1
        value: 0
        hub: polo

- alias: 'Activate normal mode'
  trigger:
    - platform: time
      at: '06:00:10'
  condition:
    - condition: numeric_state
      entity_id: sensor.weather_today_max_temp
      below: 27
  action:
    - service: modbus.write_register
      data:
        address: 2
        unit: 1
        value: 0
        hub: polo
    - delay: 00:00:10
    - service: modbus.write_register
      data:
        address: 3
        unit: 1
        value: 0
        hub: polo
    - delay: 00:00:10
    - service: modbus.write_register
      data:
        address: 4
        unit: 1
        value: 2
        hub: polo

- alias: 'Activate Eco Mode when it is hot'
  trigger:
    platform: numeric_state
    entity_id: sensor.polo_tempairoutside
    above: 300   # Air sucked in is over 30°C
  condition:
    - condition: numeric_state
      entity_id: sensor.polo_automode
      below: 1
    - condition: numeric_state
      entity_id: sensor.polo_ecomode
      below: 1
  action:
    - service: modbus.write_register
      data:
        address: 2
        unit: 1
        value: 1
        hub: polo

- alias: 'Deactivate Eco Mode'
  trigger:
    platform: numeric_state
    entity_id: sensor.polo_tempairoutside
    below: 280
    for: "01:00:00"
  condition:
    - condition: numeric_state
      entity_id: sensor.polo_automode
      below: 1
    - condition: numeric_state
      entity_id: sensor.polo_ecomode
      above: 0
  action:
    - service: modbus.write_register
      data:
        address: 2
        unit: 1
        value: 0
        hub: polo

Example of standard Lovelace cards to display interesting information and set modes:

cards:
  - cards:
      - entity: sensor.tpolo_fanairin
        max: 100
        min: 0
        severity:
          green: 30
          red: 75
          yellow: 50
        type: gauge
      - entity: sensor.polo_currentmode
        max: 4
        min: 0
        name: Modus
        severity:
          green: 1
          red: 3
          yellow: 2
        type: gauge
      - entities:
          - entity: sensor.polo_ecomode
            hold_action:
              action: call-service
              service: modbus.write_register
              service_data:
                address: 2
                hub: polo
                unit: 1
                value: 0
            icon: 'mdi:tree-outline'
            tap_action:
              action: call-service
              service: modbus.write_register
              service_data:
                address: 2
                hub: polo
                unit: 1
                value: 1
          - entity: sensor.polo_automode
            hold_action:
              action: call-service
              service: modbus.write_register
              service_data:
                address: 3
                hub: polo
                unit: 1
                value: 0
            icon: 'mdi:clock-outline'
            tap_action:
              action: call-service
              service: modbus.write_register
              service_data:
                address: 3
                hub: polo
                unit: 1
                value: 1
        show_name: false
        type: glance
    type: horizontal-stack
  - cards:
      - icon: 'mdi:home-floor-1'
        name: Away
        tap_action:
          action: call-service
          service: modbus.write_register
          service_data:
            address: 4
            hub: polo
            unit: 1
            value: 1
        type: button
      - icon: 'mdi:home-floor-2'
        name: Normal
        tap_action:
          action: call-service
          service: modbus.write_register
          service_data:
            address: 4
            hub: polo
            unit: 1
            value: 2
        type: button
      - icon: 'mdi:home-floor-3'
        name: Intensive
        tap_action:
          action: call-service
          service: modbus.write_register
          service_data:
            address: 4
            hub: polo
            unit: 1
            value: 3
        type: button
      - icon: 'mdi:home-plus'
        name: Boost
        tap_action:
          action: call-service
          service: modbus.write_register
          service_data:
            address: 4
            hub: polo
            unit: 1
            value: 4
        type: button
    type: horizontal-stack
  - entities:
      - entity: sensor.tpolo_tempairoutside
      - entity: sensor.tpolo_tempairout
      - entity: sensor.tpolo_tempairin
      - entity: sensor.polo_energy
      - entity: sensor.polo_heater
      - entity: sensor.polo_heatexenergy
      - entity: sensor.polo_heatexefficiency
    hours_to_show: 36
    refresh_interval: 0
    type: history-graph
title: Ventilation
type: vertical-stack

Should look like this:

I’m not an expert on Modbus, but if you have any questions let me know…

1 Like

Hi NamCisum,

very nice I own a Polo air 250+ and I try to get running it.
Where do I need to do the register call?

Thanks and cheers

Not quite sure what you want to know? You can call the service write_register wherever you call services in HAss (i.e. scripts, automations, etc) to change something. If you want to read a register you use the modbus platform and create sensors for it (see examples above).

got it…thanks!

NamCisum

I added the modbus settings to configuration.yaml and restarted hassio but I couldn’t see the entities. Any hint?

Thanks and cheers

Have you added the sensor config as well (see second code snippet above)?

Any errors in the log?

yes, I did.
No errors in the log.

Well, it is a bit hard to help if the only thing you tell me is that it doesn’t work. :slight_smile:
At least post your config…

true.
Thanks for a review!

image

Looks good to me…

Is the IP / the subnet reachable from HAss (can you ping it from HAss CLI)?

If you set in configuration.yaml

logger:
  default: info

do you get the following lines in log after restart?

2020-10-13 09:30:57 INFO (MainThread) [homeassistant.bootstrap] Setting up stage 2: {-List includes modbus-}
2020-10-13 09:30:57 INFO (MainThread) [homeassistant.setup] Setting up modbus
2020-10-13 09:31:04 INFO (MainThread) [homeassistant.setup] Setup of domain modbus took 6.1 seconds
2020-10-13 09:31:08 INFO (MainThread) [homeassistant.components.sensor] Setting up sensor.modbus
2020-10-13 09:31:08 INFO (MainThread) [homeassistant.components.sensor] Setting up sensor.modbus

If that doesn’t help set logger like this:

logger:
  logs:
    homeassistant.components.modbus: debug
    pymodbus.client: debug

…and paste the logs here.

hey hey,

that’s all I get after I’ve set the logger to debug level

‘021-02-24 16:05:41 INFO (MainThread) [homeassistant.setup] Setting up modbus’

Are the template sensors being setup and showing nothing / unavailable?

If not: Is the sensors.yaml correctly referenced in configuration.yaml:

sensor: !include sensors.yaml

If yes: It appears modbus is not being setup correctly, but it doesn’t show any error / warning in the log. I can only refer you to opening an issue on GitHub for someone to look at as I can’t debug the integration as such.

Also: Have you tried pinging the IP of your PoloAir from HA CLI?

hey hey,

good news…I made it finally…:wink:
Did a complete new setup of home assistant.

I have the following issue…history is not loading? stucks at loading state history…
As well the tab to activate/deactivate eco mode and auto mode doesn’t react really properly (very slow or sensor gets unavailable for a certain time of period).

I have the polo air 250+ in use…but should work the same as your 450.
Have you tried to read out the schedule?

anyway thank you very much for your work and being patient ;).UI looks better than the native mobile app :wink:

cheers

History is a HA component that has nothing to do with ModBus - the sensors need to record to your database, then you can view the historic data.

I don’t think the Air 250+ is any different - it appears to be connectivity issues or the ModBus Slave (the Polo Air) not reacting anymore.

Good luck with tracking down those issues!

Hi @NamCisum ,

how are you?
I have an issue again with the integration as it ran for a while properly.

the sensors are no more available. Guess it was due to an upgrade…?

the message is like:
Entität ist derzeit nicht verfügbar: sensor.tpolo_fanairin
(Entity is currently not available…)

And in the settings → entities: this entity (“sensor.tpolo_fanairin”) has no unique ID, …

any ideas how to fix that issue?

thank you very much!

cheers

Are you aware of the changes to the Modbus syntax in YAML introduced earlier this year and made mandatory in 2021.7 (I believe)? The YAML above is no longer valid, I will change it asap…

I updated the first post with new YAML…

Hi @NamCisum

very kind of you.
I’m back in the game.
I’ve missed the change totally. Thanks for the hint!
So I’ve updated my config and outsourced the modbus code in a separated file.

It could make sense to convert the unit of measurements for a few sensors from W to kW/kWh to integrate them in the energy dashboard as single consumer.

What do you think?

thanks again and cheers

As far as I know the Energy Dashboard accepts only Entities compliant with certain standards - no idea if you can do that using a template sensor. Feel free to give it a try and let us know…

hi,

are you use the panel humidity sensor and temp sensor for showing?
somehow comma/dot is missing.
image
tried to format as float but without affect.

thanks and cheers

I use a template sensor:

    tpolo_temppanel:
      friendly_name: 'Panel'
      device_class: temperature
      unit_of_measurement: '°C'
      value_template: '{{ states.sensor.polo_temppanel.state | float / 10 }}'