ESP Haier: Haier Air Conditioner + ESP Home + Wemos D1 mini

This version is working for me: I can change modes, fan speed, outer flap position horizontal/vertical. So I’m happy with this.
Do you think there will be a way to implement left/right swing with this version? Please let me know if there’s anything I can do!

esp32:
  board: nodemcu-32s

uart:
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 16
  id: ac_port  

logger:
  level: DEBUG
  baud_rate: 0

web_server:

wifi:
  ap:

api:

ota:

climate:
  - platform: haier
    name: ${device_name}
    uart_id: ac_port

I wanted to test with esp32 today - config above should work, righ? so most likely i did not solder something right, right?

in the log i see only

Message sent: FF FF 0A 00 00 00 00 00 01 01 4D 01 5A

also not sure if i really have nodemcu-32s, but i guess it should not matter for this

I mean the RX TX pins are after D21 …

update figured it out:

uart:
  baud_rate: 9600
  tx_pin: 1
  rx_pin: 3
  id: ac_port  

I got confused and used 16 and 17 from config for haier board :man_facepalming:

So far good on esp32 everything (that should work now) looks good

This is how nodeMCU looks in the AC - nice fit

this is backside (ugh) - yes, first time me using hot glue, second time soldering

works perfect

1 Like

Thanks for testing. If you could help in the future with testing would be great
.

Yes it should work with both ESP8266 and ESP32 (also with both arduino and IDF frameworks)

Thanks for testing!

1 Like

I tried this version on a Haier Flair. It doesn’t work. I think my airco is using another protocol…
Originally i have the haier ESP32 1.1. Now i installed a WEMOS mini D1.

This is working but is not perfect.

I did try to see in hON log which bits change for ION (health) and health airflow and did screenshots of the log here hON tests - Album on Imgur . I think these might be quite useful for more people. At least I think the health airflow positions are quite useful, especially on bigger units it offers options for heating almost directly under the AC.

Also I noticed that with beeper off display is also off - “light” on AC remote (which i like, just noting it for others)

I also want to try to capture some positions and create preset for it, but that is just for me, I will see how it goes with me and bitfield.

Yes, something fishy is going on with bits on other protocols also. I will try to reverse code firmware again to understand how the native board works with it. Just waiting for some equipment I ordered (of course from china ESP-Prog — PlatformIO v6.1 documentation )

Thanks for that. I will check it and will try to support it soon.

Thanks. It would be very useful.

1 Like

Hey paveldn,

Thanks for testing. If you could help in the future with testing would be great

Is there any update on the Github/code? I’m more than happy to test out any update/change in the code, especially if it adds left/right swing :pleading_face: :smiley:

Many thanks in advance!

Sorry, but there is nothing to update yet. One of the problems I am facing with my own ACs now is that I often get an E7 error from AC (an error in communication with an external module) and I have no idea how to fix it. So mostly now I trying again to reverse-engineer protocol. Usually, such things take some time.

1 Like

It is a big problem. The most times is a fault in outdoor PCB board, the power supply.

1 Like

I have a FTDI adapter. What more do I need to flash the ESP32 Haier module?

FTDI is enough, I am also using this kind of device

But you can just solder whires to test points

I need advice: I am not really using swing mode so don’t really know what everybody expects from it. I can do something like this, add additional select item that will allow to select direction for air in case if swing is disable:
image
Will it be what everybody expects or do you have another idea in your head?

1 Like

This would be great for me, but then for Horizontal swing because vertical swing can already be set from the climate.airco entity. But the idea of a seperate entity for additional control will be awesome!
Thank you for your hard work paveldn!

1 Like

This gave me “idea” that health airflow could actually be part of airflow position like most top and most bottom, honestly makes much more sense then the marketing health airflow thing.

Hi everybody,

I updated dev branch for hOn
Included changes to be able to configure airflow direction in case if swing is turned off
To find these settings you need to do the following steps:


There is only Left/Center/Right and Up/Center/Down but I have plans to add more options including health airflow (as @evlo proposed) later (need to find a good way to do it). But for now please test this.
Next two weeks (till the 21st of September) I will be on vacation, but I will check all your comments, remarks, and suggestions when I will be back.
Best regards,
Pavlo

1 Like

Hey Pavlo,

I’m a bit confused :confused: I’ve used this version for the past weeks, but it looks like this updated code is for the original Haier ESP32 WiFi module.
Is this compatible? Can I use this code with a generic ESP32 or with a Wemos D1 mini?

Hi,
There are 2 (at least) different versions of the protocol one is so called smartAir2 and another one is hOn (I am using names of Haier applications that work with these protocols). They are not compatible (at least for now). The one that you are using is smartAir2. The one that I updated is for hOn with the original ESP wifi module. Sorry, but I will always try new features on the hOn version just because I have this AC installed and for smartAir2 I don’t have them and I need to ask people to test for me. Also, you should consider yaml file that is included in the repo more as example. Code is written in a way that it should support any ESP8266 or ESP32 with both frameworks Arduino or IDF. So you should adopt it to your ESP device.

Alright, I’ll try it out and let you know about any results.
Again, thank you very much for all your work. Enjoy your holiday :slight_smile:

Does someone has a ESPHome config for the original ESP32 Haier board?
I now use a d1_mini but want to swap them.

esphome:
  name: woonkamer-airco
  platform: ESP8266
  board: d1_mini
  includes:
    - Haierv2.h
globals:
  - id: control_command 
    type: byte[64]
    restore_value: no
    initial_value: '{HEADER,HEADER,20,64,0,0,0,0,0,1,SEND_COMMAND}' #this is the header, the rest of the message is the status/control
    
logger:
  level: DEBUG
  baud_rate: 0 #Important. You can't use serial port
  
wifi:
  ssid: "*********"
  password: "**********"

  manual_ip:
    static_ip: 192.168.2.80
    gateway: 192.168.2.254
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "************"
    password: "************"
    
captive_portal:
    
api:
# Enable Home Assistant API
ota:

web_server:

switch:
  - platform: template
    name: "Screen"
    id: display_status
    icon: mdi:monitor-eye
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], DISPLAY_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], DISPLAY_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], DISPLAY_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Use Fahrenheit"
    id: units_status
    icon: mdi:temperature-fahrenheit
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], F_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], F_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], F_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Health Mode"
    id: purify_status
    icon: mdi:shimmer
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA2_OFFSET], HEALTH_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA2_OFFSET], HEALTH_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA2_OFFSET], HEALTH_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Self Clean"
    id: self_clean_status
    icon: mdi:liquid-spot
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA3_OFFSET], SELF_CLEAN_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA3_OFFSET], SELF_CLEAN_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA3_OFFSET], SELF_CLEAN_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Steri Clean 56C"
    id: steri_clean_status
    icon: mdi:liquid-spot
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], SELF_CLEAN56_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], SELF_CLEAN56_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], SELF_CLEAN56_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Controls Lock"
    id: lock_status
    icon: mdi:remote-off
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA2_OFFSET], LOCK_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA2_OFFSET], LOCK_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA2_OFFSET], LOCK_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Supplamental Heat"
    id: supp_heat
    icon: mdi:heating-coil
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA2_OFFSET], ELECTRIC_HEAT_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA2_OFFSET], ELECTRIC_HEAT_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA2_OFFSET], ELECTRIC_HEAT_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Beeper"
    id: echo_status
    icon: mdi:volume-high
    restore_state: true
    optimistic: true
  - platform: template
    name: "Fresh Air"
    id: fresh_mode
    icon: mdi:weather-windy
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA3_OFFSET], FRESH_AIR_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA3_OFFSET], FRESH_AIR_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA3_OFFSET], FRESH_AIR_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Light Status"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA3_OFFSET], LIGHT_STATUS_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA3_OFFSET], LIGHT_STATUS_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA3_OFFSET], LIGHT_STATUS_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Energy Save Status"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA3_OFFSET], ENERGY_SAVE_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA3_OFFSET], ENERGY_SAVE_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA3_OFFSET], ENERGY_SAVE_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Filter"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA3_OFFSET], FILTER_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA3_OFFSET], FILTER_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA3_OFFSET], FILTER_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Intelligence"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], INTELLIGENCE_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], INTELLIGENCE_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], INTELLIGENCE_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Pulse Motor Valve"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], PMV_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], PMV_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], PMV_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Energy Save"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], ENERGY_SAVE_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], ENERGY_SAVE_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], ENERGY_SAVE_BIT);
          sendData(id(control_command));
  - platform: template
    name: "Half Degree"
    lambda: |-
        return bitRead(id(control_command)[STATUS_DATA1_OFFSET], HALF_DEGREE_BIT);
    turn_on_action:
      - lambda: |-
          bitSet(id(control_command)[STATUS_DATA1_OFFSET], HALF_DEGREE_BIT);
          sendData(id(control_command));
    turn_off_action:
      - lambda: |-
          bitClear(id(control_command)[STATUS_DATA1_OFFSET], HALF_DEGREE_BIT);
          sendData(id(control_command));

sensor:
  - platform: template
    id: outdoor_temp
    name: "Outdoor Temperature"
    update_interval: 5s
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    lambda: |-
            return ((id(control_command)[OUTDOOR_TEMP])/2.0)-20; //offset of 25

climate:
  - platform: custom
    lambda: |-
      auto haier = new Haier();
      App.register_component(haier);
      return {haier};
    climates:
      - name: "Woonkamer Airco"

Can i just change platform and board???

What kind of original board do you have?
There is 2 types:

  1. image
  2. image
    I know nothing about #2 but if you have 1 it is not enough just to change board type. The problem is that with d1 mini you are using UART0 to talk to AC and with haier ESP32 you need to use UART2. I don’t know if there is a way to reconfigure UART0 to use pins 16 and 17. Also esp32 haier is single core board. I didn’t find a way to build it with arduino framework so you will need to use idf framework. And code you are using will not work with idf. I would recomend you to use my code with this board https://github.com/paveldn/ESP32-S0WD-Haier/tree/dev it was created specially for it