Fairland heat pump to HA

I bought a pool heat pump from Meranus at the beginning of this year. Since this has no wifi integrated, I wanted to save myself the wifi-box and integrate the heat pump directly via modbus in homeassistant. I was able to find out that the manufacturer of the device is aquark from China and emailed to ask for the mode registers. I received exactly the same list as already mentioned here in the thread by Medved. With the help of the addresses I now tried to read out the data via a Mobus to TCP adapter. I use a Protoss PE11-H. After a few tries everything worked so far. To calculate the correct temperatures the offset and scale must be set. For example, the value 120 stands for 30 degrees Celsius. The calculation is as follows: (120 / 2) - 30 = 30

Enclosed the extract from my config.yaml, maybe it helps someone here. The Climates-Device I currently use only to set the setpoint temperature, I want to pack everything in a complete template in the next time.

# configuration.yaml
modbus:
  - type: tcp
    host: 192.168.0.xx
    port: 502
    name: "Protoss"
    climates:
      - name: "Meranus"
        slave: 1
        address: 3
        input_type: input
        data_type: int16
        max_temp: 40
        min_temp: 18
        offset: -30
        precision: 1
        scale: 0.5
        target_temp_register: 3
        temp_step: 1
        temperature_unit: C
        hvac_mode_register:
          address: 1
          write_registers: false
          values:
            state_auto: 1
            state_heat: 0
        hvac_onoff_register: 0
        write_registers: false
    switches:
      - name: ME6_Power
        unique_id: ME6_Power
        slave: 1
        address: 0
        command_on: 1
        command_off: 0
        write_type: coil
        verify:
            input_type: coil
            address: 0
            delay: 1
            state_on: 1
            state_off: 0
    binary_sensors:
      - name: ME6_On_Off
        unique_id: ME6_DI_0
        slave: 1
        address: 0
        input_type: discrete_input
      - name: ME6_Defrosting
        unique_id: ME6_DI_1
        slave: 1
        address: 1
        input_type: discrete_input
      - name: ME6_Error_E0
        unique_id: ME6_DI_48
        slave: 1
        address: 48
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E1
        unique_id: ME6_DI_49
        slave: 1
        address: 49
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E2
        unique_id: ME6_DI_50
        slave: 1
        address: 50
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E3
        unique_id: ME6_DI_51
        slave: 1
        address: 51
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E4
        unique_id: ME6_DI_52
        slave: 1
        address: 52
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E5
        unique_id: ME6_DI_53
        slave: 1
        address: 53
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E6
        unique_id: ME6_DI_54
        slave: 1
        address: 54
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E7
        unique_id: ME6_DI_55
        slave: 1
        address: 55
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E8
        unique_id: ME6_DI_56
        slave: 1
        address: 56
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_E9
        unique_id: ME6_DI_57
        slave: 1
        address: 57
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_EA
        unique_id: ME6_DI_58
        slave: 1
        address: 58
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_EB
        unique_id: ME6_DI_59
        slave: 1
        address: 59
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_ED
        unique_id: ME6_DI_61
        slave: 1
        address: 61
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P0
        unique_id: ME6_DI_64
        slave: 1
        address: 64
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P1
        unique_id: ME6_DI_65
        slave: 1
        address: 65
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P2
        unique_id: ME6_DI_66
        slave: 1
        address: 66
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P3
        unique_id: ME6_DI_67
        slave: 1
        address: 67
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P4
        unique_id: ME6_DI_68
        slave: 1
        address: 68
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P5
        unique_id: ME6_DI_69
        slave: 1
        address: 69
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P6
        unique_id: ME6_DI_70
        slave: 1
        address: 70
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P7
        unique_id: ME6_DI_71
        slave: 1
        address: 71
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P8
        unique_id: ME6_DI_72
        slave: 1
        address: 72
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_P9
        unique_id: ME6_DI_73
        slave: 1
        address: 73
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_PA
        unique_id: ME6_DI_74
        slave: 1
        address: 74
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F0
        unique_id: ME6_DI_80
        slave: 1
        address: 80
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F1
        unique_id: ME6_DI_81
        slave: 1
        address: 81
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F2
        unique_id: ME6_DI_82
        slave: 1
        address: 82
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F3
        unique_id: ME6_DI_83
        slave: 1
        address: 83
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F4
        unique_id: ME6_DI_84
        slave: 1
        address: 84
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F5
        unique_id: ME6_DI_85
        slave: 1
        address: 85
        input_type: discrete_input
        device_class: problem        
      - name: ME6_Error_F6
        unique_id: ME6_DI_86
        slave: 1
        address: 86
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F7
        unique_id: ME6_DI_87
        slave: 1
        address: 87
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F8
        unique_id: ME6_DI_88
        slave: 1
        address: 88
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_F9
        unique_id: ME6_DI_89
        slave: 1
        address: 89
        input_type: discrete_input
        device_class: problem
      - name: ME6_Error_FB
        unique_id: ME6_DI_91
        slave: 1
        address: 91
        input_type: discrete_input
        device_class: problem
    sensors:
      - name: ME6_Mode
        unique_id: ME6_HR_0
        slave: 1
        address: 0
        input_type: holding
        data_type: int16
      - name: ME6_Silence
        unique_id: ME6_HR_1
        slave: 1
        address: 1
        input_type: holding
        data_type: int16
      - name: ME6_Set_temp
        unique_id: ME6_HR_3
        slave: 1
        address: 3
        input_type: holding
        data_type: int16
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C
      - name: ME6_Speed
        unique_id: ME6_IR_0
        slave: 1
        address: 0
        input_type: input
        data_type: int16
      - name: ME6_Water_inlet_temp
        unique_id: ME6_IR_3
        slave: 1
        address: 3
        input_type: input
        data_type: int16
        precision: 1
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C
      - name: ME6_Water_outlet_temp
        unique_id: ME6_IR_4
        slave: 1
        address: 4
        input_type: input
        data_type: int16
        precision: 1
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C
      - name: ME6_Outside_temp
        unique_id: ME6_IR_5
        slave: 1
        address: 5
        input_type: input
        data_type: int16
        precision: 1
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C
      - name: ME6_min_Set_temp
        unique_id: ME6_IR_17
        slave: 1
        address: 17
        input_type: input
        data_type: int16
        precision: 0
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C
      - name: ME6_max_Set_temp
        unique_id: ME6_IR_18
        slave: 1
        address: 18
        input_type: input
        data_type: int16
        precision: 0
        offset: -30
        scale: 0.5
        device_class: temperature
        unit_of_measurement: °C

#switch.yaml
  - platform: template
    switches:
      silentmode_poolheizung:
        friendly_name: "Silentmode Poolheizung"
        value_template: "{{ is_state('sensor.me6_silence', '1') }}"
        turn_on:
          service: modbus.write_register
          data:
            hub: Protoss
            slave: 1
            address: 1
            value: 1
        turn_off:
          service: modbus.write_register
          data:
            hub: Protoss
            slave: 1
            address: 1
            value: 0

2 Likes

I’m not sure if I’ll connect mine with modbus but thumbs up for this integration and modbus info.

1 Like

I have now removed the climate-part of the modbus-integration and bundled the control of the heat pump completely in one device with the HACS Template Climate. For this I had to create three scripts to control temperature, silent mode and main switch. Now I’m happy with the integration so far.

If anyone is interested here’s the code:

configuration.yaml

climate:
  - platform: climate_template
    name: Poolwärmepumpe
    unique_id: meranus_poolheizung
    modes:
      - "off"
      - "heat"
    fan_modes:
      - "low"
      - "high"
    min_temp: 18
    max_temp: 40
    # get current temp.
    current_temperature_template: "{{ states('sensor.me6_water_inlet_temp') }}"
    target_temperature_template: "{{ states('sensor.me6_settemp_heat') }}"
    fan_mode_template: "{% if is_state('sensor.me6_silence', '1') -%} low {%- else -%} high {%- endif %}"
    hvac_mode_template: "{% if is_state('switch.me6_power', 'on') -%} heat {%- else -%} off {%- endif %}"
    precision: 0.1
    temp_step: 1
    # fan_mode action (silence)
    set_fan_mode:
      # send the silentmode state to modbus-device
      - service: script.meranus_set_silent
        data:
          silent: "{{ fan_mode }}"
    set_hvac_mode:
      # send the silentmode state to modbus-device
      - service: script.meranus_set_power
        data:
          power: "{{ hvac_mode }}"
    set_temperature:
      # send the silentmode state to modbus-device
      - service: script.meranus_set_temp
        data:
          temp: "{{ temperature }}"

scripts:

alias: meranus_set_power
sequence:
  - variables:
      val_power: >-
        {% if power == 'heat' -%} {{(1 | int)}} {%- else -%} {{(0 | int)}} {%-
        endif %}
  - condition: template
    value_template: >-
      {{ False if bool(states("switch.me6_power")) == bool(val_power) else True
      }}
  - service: modbus.write_coil
    data:
      hub: Protoss
      address: 0
      slave: 1
      state: "{{ val_power }}"
mode: single
alias: meranus_set_silent
sequence:
  - variables:
      val_silence: >-
        {% if silent == 'low' -%} {{(1 | int)}} {%- else -%} {{(0 | int)}} {%-
        endif %}
  - condition: not
    conditions:
      - condition: template
        value_template: "{{ is_state(\"sensor.me6_silence\", val_silence | string) }}"
  - service: modbus.write_register
    data:
      hub: Protoss
      slave: 1
      address: 1
      value: "{{ val_silence }}"
mode: single
alias: meranus_set_temp
sequence:
  - condition: not
    conditions:
      - condition: template
        value_template: "{{ is_state(\"sensor.me6_settemp_heat\", temp | int | string) }}"
  - service: modbus.write_register
    data:
      hub: Protoss
      address: 3
      slave: 1
      value: "{{ ((temp + 30) * 2 ) | int }}"
mode: single

Hi Medved, I’m facing the same problem with actual watter temperature. I would say it show surrounding temperature. Have you found a solution allready ?

The solution found here . Using Local Tuya . OMG - Tuya local HACS integration for Tuya devices in Home Assistant - YouTube obrazek

1 Like

I was now able to add the Inverter, but can only see the Power on/off device :frowning:
The Tuya Local allows me to see all available parmaters but when I select type “climate” and fill all paramteres it always deliveres an unavailable device entity

Its working for me with the tuya local intergration that xmarina refers to in the youtube link!
We’ve got a Fairland IPHCR26 but the integration picks it up as a iphcr15 which still works.

Only thing indeed is that the water temperature is showing as 11 degrees.

1 Like

OMG slved the set temperature and inteligent mode! It was so simple!

For my Fairland the updateable settings are nummers: 106, 117

1 : Power (ON/off)
102 : Inlet Water Temperature (30)
103 : Temperature Switch (True)
104 : Snelheidspercentage (%)
105 : Select Mode
106 : Setting (32 doel temp)
107 : Set Value Lower Limit (18)
108 : Set Value Upper Limit (40)
115 : Fault (0)
116 : Fault 2 (0)
117 : Silence Mode (Silence/Smart)
118 : Heating or Cooling Running (ON/off)
120 : Outer Coil Temperature (3 / 4)
122 : Exhaust Temperature (67)
124 : Ambient temp
125 : Compressor Running Speed
126 : Compressor Current (60) A?
127 : Cooling Plate Temperature (34)
128 : Electronic Expansion Valve Opening (95)
129 : DC Fan motor speed (rpm)
130 : Defrost (ON/off)
134 : Comoressor AC Contactor
135 : Circulating Pump
136 : Four-way Valve
139 : Current-limiting Charging Relay
140 : AC Fan Motor Speed

@dictus.jurgen Where did you get that list of settings / registers from? I can’t seem to find it.

Saw a video about the Tuya iOT platform and was able to connect my device. Then went over all possible port numbers and wrote the answers down and compared them to the info that the Fairland app provided.

So basically trial and error

PS: I used it all summer and it worked great

Hi @dictus.jurgen

Are you actually getting values for all these datapoints you listed above via the Tuya platform?

For me when I check for example ‘Compressor Current’ under the debug screen I don’t get any values back. In fact for most datapoints listed I don’t get anything back…

Hi, Yes I was :-(. my Fairland remote died on me this winter. So now I no longer get anything

Hello Christian, I am facing the same problem as you. I have already connected my Bayrol Salt Relax Pro to Home Assistant via Modbus/ESP32, which works very well. Now I would also like to connect my Meranus ECO 10 via Modbus, like you did. Could you possibly provide step-by-step instructions (wiring, components, programming, etc.)? That would help me a lot. Thx.

For people also having this issue, the external Fairland Wifi antenna was version XXXX-2019X XXX and not yet Tuya, my newer one is XXXX-2020X XXX and works, but already broken after 2 years, ordered a new 2020 one.

Hi @dictus.jurgen , I’m trying to find this external antenna but no luck anywhere. Can you share a link please?

1 Like

I am from Belgium and bought mine at Zwemco.be (Link)

Be aware that if you buy the module somewhere else it has to be the WY??? model and not the WF??? Variant (a bit older) or you will only be able to use the app and not the Tuya integration.

Good luck

Thanks Jurgen, look at that, I’m from Belgium as well.
I have the Wifi built-in and thought you could attach an extra antenna, this doesn’t seem to be the case.
I’m gonna give below solution a go. See if that works.
Controlling a Fairland Pool Heatpump eliminating Tuya - Share your Projects! - Home Assistant Community (home-assistant.io)

1 Like

Hello, I wanted to buy the Tuya module for my 2019 IPHCR40 heat pump. The site indicated does not ship to Italy. Do you happen to have the product code so you can order it in another store and have it shipped to Italy. Thank you

Hello Asino123
Mine is the Tuya Fairland WiFi module WY40B (AQ2303160414-20200109)

image

This solved for me.

I have a Fairland X20 Inverter pump and was struggling to get it into HA.

for context, the pump was easily added to the Smart Life App, and all worked there correctly.
now, for the HA part…
1 - Official Tuya integration: it “finds” the pump and adds it, but it’s a device without any entities in it. you can’t do anything.
2 - Then I tried a local tuya version that would autorecognize the device IDs and etc. It couldn’t add it. it didn’t work. I saw myself trying to mess with DPs with trial and error.
3 - That’s when I saw the video form the post above. The Tuya Local integration (that I learned is different from local tuya) had native support for it. It worked as a charm.