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

I have the original ESP32-for-Haier module on my AC, I haven’t yet tried connecting any other “homemade” versions. I can connect a logic analyzer and provide logs if it’ll help in finding out how the original module communicates with the AC.

I don’t know what is “original ESP32-for-Haier module”. I only know the kzw-w001 and kzw-w002

This project is for a homemade replacement of kzw-w001 or 2. I don’t know if it works with others.

Hello everyone, I also own a Tundra Plus unit with the ESP32-for-haier module.
Maybe it is not possible to communicate because the tx and the rx are shifted to 5V?
Unfortunately I don’t have an oscilloscope.

Greetings.

Scroll up, there’s a new module they’re shipping with recent AC models. I have a logic analyzer and I’ll try to see what that new module does.

I haven’t had time to do an analysis with the AC connected, but the ESP32-for-Haier itself provides some valuable info. The TXD0 serves as a debug port at 115200, whereas MCU_TX communicates with the AC at 9600.
Here’s a truncated debug port boot log:

Jun  8 2016 00:22:57

 rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
 configsip: 0, SPIWP:0xee
 clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
 mode:DIO, clock div:2
 load:0x3fff0018,len:4
 load:0x3fff001c,len:600
 load:0x40078000,len:9076
 load:0x40080400,len:5168
 entry 0x40080648
 ;+ښj%(536) cpu_start: cpu freq: 80
 I (536) cpu_start: Pro cpu up.
 I (536) cpu_start: Application information:
 I (537) cpu_start: Project name:     esp-idf
 I (542) cpu_start: App version:      1f794c834
 I (547) cpu_start: Compile time:     Apr 18 2021 20:49:15
 I (553) cpu_start: ELF file SHA256:  aaa6972fd7d3769c...
 I (559) cpu_start: ESP-IDF:          v3.3.5
 I (564) cpu_start: Single core mode
 I (568) heap_init: Initializing. RAM available for dynamic allocation:
 I (575) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
 I (581) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
 I (587) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
 I (594) heap_init: At 3FFBDB5C len 00000004 (0 KiB): DRAM
 I (600) heap_init: At 3FFCCB28 len 000134D8 (77 KiB): DRAM
 I (606) heap_init: At 3FFE0440 len 0001FBC0 (126 KiB): D/IRAM
 I (612) heap_init: At 40078000 len 00008000 (32 KiB): IRAM
 I (618) heap_init: At 4009E118 len 00001EE8 (7 KiB): IRAM
 I (624) cpu_start: Pro cpu start user code
 j%(643) cpu_start: Starting scheduler on PRO CPU.

 app_main --- APPLICATION START ---
 RBJwifi driver task: 3ffcfa20, prio:23, stack:3584, core=0
 I (679) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
 I (679) system_api: Base MAC address is not set, read default base MAC address from BLK0 gf EFUSE
 I (689) wifi:wifi firmware version: dc30037
 I (689) wifi:config NVS flash: disabled
 I (699) wifi:config nano formating: disabled
 I (699) wifi:Init data frame dynamic rx buffer num: 32
 I (709) wifi:Init management frame dynamic rx buffer num: 32
 I (709) wifi:Init management short buffer num: 32
 I (719) wifi:Init dynamic tx buffer num: 32
 I (719) wifi:Init static rx buffer size: 1600
 I (719) wifi:Init static rx buffer num: 2
 I (729) wifi:Init dynamic rx buffer num: 32
 I (729) wifi_init: rx ba win: 4
 I (729) wifi_init: tcpip mbox: 32
 I (739) wifi_init: udp mbox: 6
 I (739) wifi_init: tcp mbox: 6
 I (749) wifi_init: tcp tx win: 5744
 I (749) wifi_init: tcp rx win: 5744
 I (749) wifi_init: tcp mss: 1436
 I (759) wifi_init: WiFi IRAM OP enabled
 I (759) wifi_init: WiFi RX IRAM OP enabled
 I (769) wifi_init: LWIP IRAM OP enabled
 I (779) uart: queue free spaces: 20
 I (779) epp_parser_map: epp_parser_state.downloaded = 1
 I (779) epp_parser_map: Mapped partition to data memory address 0X3f400000
 I (789) epp_parser_map: free heap = 196592
 I (789) epp_parser_map: Mapped partition to data memory address 0x3f448000
 I (799) epp_parser_map: map partition -> free heap = 196568
 1mE (809) uplus_sys_res_area_get_size:
 E (809) uplus_sys_res_area_read: offset = 0, len = 256
 E (819) uplus_sys_res_area_write: offset = 0, len = 256
 I (819) uplus_sys_res_area_write: 0x3ffb4e18   00 00 00 00 73 6f 68 75  00 00 00 00 00 00 00 00  |....sohu........|
 I (839) uplus_sys_res_area_write: 0x3ffb4e28   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (849) uplus_sys_res_area_write: 0x3ffb4e38   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (859) uplus_sys_res_area_write: 0x3ffb4e48   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (869) uplus_sys_res_area_write: 0x3ffb4e58   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (879) uplus_sys_res_area_write: 0x3ffb4e68   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (889) uplus_sys_res_area_write: 0x3ffb4e78   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (899) uplus_sys_res_area_write: 0x3ffb4e88   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (909) uplus_sys_res_area_write: 0x3ffb4e98   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (919) uplus_sys_res_area_write: 0x3ffb4ea8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (929) uplus_sys_res_area_write: 0x3ffb4eb8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (949) uplus_sys_res_area_write: 0x3ffb4ec8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (959) uplus_sys_res_area_write: 0x3ffb4ed8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (969) uplus_sys_res_area_write: 0x3ffb4ee8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (979) uplus_sys_res_area_write: 0x3ffb4ef8   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 I (989) uplus_sys_res_area_write: 0x3ffb4f08   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 [347]: uplus_epp_init - start serial connection
 uplug_conf_epp_data_get called
 [354]: uplug_epp_module_init - called
 E (1009) uplus_sys_res_area_write: offset = 16, len = 8
 I (1019) uplus_sys_res_area_write: 0x3ffb4e28   00 00 00 00 00 00 00 00                           |........|
 E (1029) uplus_sys_res_area_write: offset = 0, len = 8
 I (1029) uplus_sys_res_area_write: 0x3ffb4e18   02 00 00 00 73 6f 68 75                           |....sohu|
 [UHomeOS][I][391]: dev_main is 255 dev_minor is 255 dev_dedicated is 0000000000000
 ]}}͕}ձсcalled
 [UHomeOS][LOG][422]: uplugSDK e++ init ok
 [422]: uplus_epp_access_register - called
 [422]: uplus_epp_access_register - params check pass
 [425]: uplug_epp_access_init - called - access method 0
 [430]: uplug_epp_access_init - init serial access struct
 [435]: uplug_epp_access_init - call init method
 +ښj%(1109) CDM_Manager__Initialize: discovery configuration : key = eepromName, value = 00000000
 I (1119) CDM_Manager__Initialize: discovery configuration : key = acuVersion, value = 06.26.00
 I (1119) CDM_Manager__Initialize: discovery configuration : key = serialNumber, value = 999999992048011665
 I (1129) CDM_Manager__Initialize: discovery configuration : key = fwVersion, value = 4.0.0
 W (1139) uplus_sys_serial_open: esp_vfs_dev_uart_use_driver
 I (1149) CDM_Manager__Initialize: discovery configuration : key = eepromName, value = 00000000
 I (1159) CDM_Manager__Initialize: discovery configuration : key = lang, value = it-IT
 I (1169) CDM_Manager__Initialize: discovery configuration : key = chipset, value = ESP32D0WDQ5
 I (1169) CDM_Manager__Initialize: discovery configuration : key = acuVersion, value = 06.26.00
 I (1179) CDM_Manager__Initialize: discovery configuration : key = eppParserSet, value = 1

 app_main --- APPLICATION INIT COMPLETED ---

 Firmware version : IOTFW_EPP_04_00


 ----------------------- READ CERTIFICATE DATA


 *********************** host-address

 a30f6tqw0oh1x0-ats.iot.eu-west-1.amazonaws.com


 *********************** host-port

 8883


 *********************** thing-name

 c4-dd-57-45-73-74


 *********************** serial-number

 999999992048011665

 I (1229) LowPower__SleepHandler: Init low power state machine
 ;[UHomeOS][D][5131]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 61 00 07 72
 [UHomeOS][I][5132]: [UART] tx [cmd 0x61 len = 10, 13] ret 13
 [UHomeOS][D][5441]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 61 00 07 72
 [UHomeOS][I][5442]: [UART] tx [cmd 0x61 len = 10, 13] ret 13
 [UHomeOS][D][5751]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 61 00 07 72
 [UHomeOS][I][5752]: [UART] tx [cmd 0x61 len = 10, 13] ret 13
 ;ꫫ=Mum1=um061]: e++ comm time: 930
 ꫫ=Mum]um161]: get ver timeout
 [UHomeOS][I][6162]: dev_main is 255 dev_minor is 255 dev_dedicated is 0000000000000
 _ꫫ=Mum%um191]: e++ state from ver to typeid
 _[UHomeOS][D][6271]: tx serial e++ data
 ff ff 08 00 00 00 00 00 00 70 78
 [UHomeOS][I][6272]: [UART] tx [cmd 0x70 len = 8, 11] ret 11
 [UHomeOS][D][6581]: tx serial e++ data
 ff ff 08 00 00 00 00 00 00 70 78
 [UHomeOS][I][6582]: [UART] tx [cmd 0x70 len = 8, 11] ret 11
 [UHomeOS][D][6891]: tx serial e++ data
 ff ff 08 00 00 00 00 00 00 70 78
 [UHomeOS][I][6892]: [UART] tx [cmd 0x70 len = 8, 11] ret 11
 ꫫ=Mum1=um201]: e++ comm time: 930
 ꫫ=Mum]um721]: get typeid timeout
 [UHomeOS][I][7722]: dev_main is 255 dev_minor is 255 dev_dedicated is 0000000000000
 ꫫ=Mum%um751]: e++ state from typeid to status
 [UHomeOS][D][7891]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][7892]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 [UHomeOS][D][8201]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][8202]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 [UHomeOS][D][8512]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][8512]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 [UHomeOS][D][8821]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][8822]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 ?[UHomeOS][D][9131]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][9132]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 [UHomeOS][D][9441]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][9442]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 [UHomeOS][D][9751]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][9752]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 _[UHomeOS][D][10061]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][10062]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 ;[UHomeOS][D][10371]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 01 4d 01 59
 [UHomeOS][I][10372]: [UART] tx [cmd 0x01 len = 10, 13] ret 13
 ꫫ=Mum1=um0681]: e++ comm time: 2790
 ꫫ=Mum]um0841]: get status timeout
 [UHomeOS][I][10842]: dev_main is 255 dev_minor is 255 dev_dedicated is 0000000000000
 [UHomeOS][D][10891]: tx serial e++ data
 ff ff 0a 00 00 00 00 00 00 61 00 07 72
 [UHomeOS][I][10892]: [UART] tx [cmd 0x61 len = 10, 13] ret 13
 uplus_dev_status called 0
 ꫫ=Mum]um1201]: serial device closed

Sometime next week I’ll do a dump while connected to the AC. If anyone’s interested in raw dumps (Saleae Logic or Pulseview), just ask.

I may be very wrong here, but doesn’t the usb connect with a simple uart with the aircon? It could work with the midea way
Bad thing is on my parents house the Haier doesn’t have a usb slot, so I’ll have to check the main board for pins to connect

It does, this has been established, what I’m trying to find out is how this new module polls information from the AC and what it receives back. The bits are clearly very similar to the previous models

New ESP32 modules works with albetaCOM v2 version: GitHub - albetaCOM/esp-haier: ESP8266 code to connect to Haier Air Conditioner ((firmware version R_1.

But when using this version there is a problem with the purify option which turns on every time after changing the settings using the diy module. Maybe somoeone can help to identify problem in code of that fork. Purify problem with some details is also reported in github isuues. I can help with debugging, but my programming skills are not good enough to solve this issue

1 Like

I’ve completed a full dump of a few configuration command sent via the hON app and the ESP32 module. I made a list of all the commands I sent, but my logic analyzer got disconnected a few times so it’s not complete.
The good thing is that the debug port logs absolutely everything, the JSON it receives from the IoT site, the decoded commands, as well as all the bytes sent and received via MCU_TX (tx_serial e+++ data) and RX (rx_serial e++ data). Hopefuly someone will be able to debug it, I don’t have the time to do it in the next couple of weeks, I can maybe do more of these data dumps if needed.
This is just a snippet, full dump is here: ESP32-for-Haier debug dump - Pastebin.com

 I (514689) uplus_epp_usr_monitor: e_EPP_Protocol_Connection_State = 10, status = 5, free heap = 74048
 I (514739) cdm_rx_task: s sFunc errors errors 00
 I (515289) cdm_rx_task: s sFunc errors errors 00
 I (515739) uplus_epp_usr_monitor: e_EPP_Protocol_Connection_State = 10, status = 5, free heap = 71932
 I (515839) cdm_rx_task: s sFunc errors errors 00
 I (515879) cdm_command_task: parameters, size = 39
 I (515899) cdm_command_task: 1 e operationName = grSetDAC
 I (515909) cdm_command_task: 1 w onOffStatus = 1, onOffStatus = true, cTab 0 0, cFunc 1 ft01 1
 I (515939) cdm_command_task: 1 w screenDisplayStatus = 1, screenDisplayStatus = true, cTab 0 0, cFunc 1 ft01 1
 I (515949) cdm_command_task: 1 w echoStatus = 0, echoStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (515989) cdm_command_task: 1 w tempSel = 22.00, targetTemperature = 22.00, cTab 0 0, cFunc 0  0
 I (515999) cdm_command_task: 1 w machMode = 4, operationMode = 4, cTab 0 0, cFunc 0  0
 I (516009) cdm_command_task: 1 w 10degreeHeatingStatus = 0, 10degreeHeatingStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516049) cdm_command_task: 1 w muteStatus = 0, muteStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516069) cdm_command_task: 1 w rapidMode = 0, rapidMode = false, cTab 0 0, cFunc 1 ft01 1
 I (516089) cdm_command_task: 1 w healthMode = 0, healthMode = false, cTab 0 0, cFunc 1 ft01 1
 I (516109) cdm_command_task: 1 w humanSensingStatus = 0, humanSensingStatus = 0, cTab 0 0, cFunc 0  0
 I (516139) cdm_command_task: 1 w selfCleaning56Status = 0, selfCleaning56Status = false, cTab 0 0, cFunc 1 ft01 1
 I (516159) cdm_command_task: 1 w selfCleaningStatus = 0, selfCleaningStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516169) cdm_command_task: 1 w cleaningTimeStatus = 0, cleaningTimeStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516199) cdm_command_task: 1 w filterChangeStatusCloud = 0, cloudFilterChangeFlag = false, cTab 0 0, cFunc 1 ft01 1
 I (516229) cdm_command_task: 1 w specialMode = 0, specialMode = 0, cTab 0 0, cFunc 0  0
 I (516239) cdm_command_task: 1 w energySavePeriod = 15, energySavePeriod = 15, cTab 0 0, cFunc 0  0
 I (516269) cdm_command_task: 1 w heatAccumulationStatus = 0, heatAccumulationStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516299) cdm_command_task: 1 w tempUnit = 0, tempUnit = 1, cTab 1 1, cFunc 0  0
 I (516319) cdm_command_task: 1 w pmvStatus = 0, pmvStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516339) cdm_command_task: 1 w intelligenceStatus = 0, intelligenceStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516389) cdm_rx_task: s sFunc errors errors 00
 I (516399) cdm_rx_task: + operationName =  -> grSetDAC, isSuperTriggerStatusChange 1
 I (516399) cdm_rx_task: isSuperTriggerStatusChange = 1, isStatusChange = 1, isTransmissionRateElapsed = 0, currentTimestamp = 1628677169, transmissionTimestamp = 1628677148, elapsed = 21, transMode = 0, transmissionRate = 3600
 I (516409) cdm_command_task: 1 w halfDegreeSettingStatus = 0, halfDegreeSettingStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516449) cdm_command_task: 1 w lockStatus = 0, lockStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516469) cdm_command_task: 1 w electricHeatingStatus = 0, electricHeatingStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516499) cdm_command_task: 1 w humiditySel = 30, targetHumidity = 30, cTab 0 0, cFunc 0  0
 I (516529) cdm_command_task: 1 w windSensingStatus = 0, windSensingStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516549) cdm_command_task: 1 w voiceSignStatus = 0, voiceSignStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516569) cdm_command_task: 1 w voiceStatus = 0, voiceStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516579) cdm_command_task: 1 w energySavingStatus = 0, energySavingStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516609) cdm_command_task: 1 w lightStatus = 0, lightStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516629) cdm_command_task: 1 w ch2oCleaningStatus = 0, ch2oCleaningStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516659) cdm_command_task: 1 w pm2p5CleaningStatus = 0, pm2p5CleaningStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516679) cdm_command_task: 1 w humidificationStatus = 0, humidificationStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516699) cdm_command_task: 1 w freshAirStatus = 0, freshAirStatus = false, cTab 0 0, cFunc 1 ft01 1
 I (516739) cdm_command_task: 1 w windSpeed = 5, windSpeed = 5, cTab 0 0, cFunc 0  0
 I (516759) cdm_command_task: 1 w windDirectionVertical = 5, windDirectionVertical = 5, cTab 0 0, cFunc 0  0
 I (516779) cdm_command_task: 1 w windDirectionHorizontal = 0, windDirectionHorizontal = 0, cTab 0 0, cFunc 0  0
 I (516799) cdm_command_task: 1 w silentSleepStatus = 1, silentSleepStatus = true, cTab 0 0, cFunc 1 ft01 1
 I (516829) cdm_command_task: 0 ? prStr = Heat
 I (516829) cdm_command_task: edgeCommand = operationName=grSetDAC
 I (516829) cdm_command_task: applianceCommand = onOffStatus=true&screenDisplayStatus=true&echoStatus=false&targetTemperature=22.00&operationMode=4&10degreeHeatingStatus=false&muteStatus=false&rapidMode=false&healthMode=false&humanSensingStatus=0&selfCleaning56Status=false&selfCleaningStatus=false&cleaningTimeStatus=false&cloudFilterChangeFlag=false&specialMode=0&energySavePeriod=15&heatAccumulationStatus=false&tempUnit=1&pmvStatus=false&intelligenceStatus=false&halfDegreeSettingStatus=false&lockStatus=false&electricHeatingStatus=false&targetHumidity=30&windSensingStatus=false&voiceSignStatus=false&voiceStatus=false&energySavingStatus=false&lightStatus=false&ch2oCleaningStatus=false&pm2p5CleaningStatus=false&humidificationStatus=false&freshAirStatus=false&windSpeed=5&windDirectionVertical=5&windDirectionHorizontal=0&silentSleepStatus=true
 I (516909) uplus_epp_usr_monitor: e_EPP_Protocol_Connection_State = 10, status = 5, free heap = 70780
 I (516909) cdm_trigger_epp_request: operationName = grSetDAC
 W (516919) uplus_epp_plugin_op: sn = 18, name = grSetDAC
 I (516959) cdm_rx_task: s sFunc errors errors 00
 I (516959) cdm_rx_task: + operationName = grSetDAC -> , isSuperTriggerStatusChange 1
 I (516969) cdm_rx_task: isSuperTriggerStatusChange = 1, isStatusChange = 1, isTransmissionRateElapsed = 0, currentTimestamp = 1628677170, transmissionTimestamp = 1628677169, elapsed = 1, transMode = 0, transmissionRate = 3600
 [UHomeOS][I][516371]: w json data : [{"name":"onOffStatus","value":"true"},{"name":"screenDisplayStatus","value":"true"},{"name":"echoStatus","value":"false"},{"name":"targetTemperature","value":"22.00"},{"name":"operationMode","value":"4"},{"name":"10degreeHeatingStatus","value":"false"},{"name":"muteStatus","value":"false"},{"name":"rapidMode","value":"false"},{"name":"healthMode","value":"false"},{"name":"humanSensingStatus","value":"0"},{"name":"selfCleaning56Status","value":"false"},{"name":"selfCleaningStatus","value":"false"},{"name":"cleaningTimeStatus","value":"false"},{"name":"cloudFilterChangeFlag","value":"false"},{"name":"specialMode","value":"0"},{"name":"energySavePeriod","value":"15"},{"name":"heatAccumulationStatus","value":"false"},{"name":"tempUnit","value":"1"},{"name":"pmvStatus","value":"false"},{"name":"intelligenceStatus","value":"false"},{"name":"halfDegreeSettingStatus","value":"false"},{"name":"lockStatus","value":"false"},{"name":"electricHeatingStatus","value":"false"},{"name":"targetHumidity","value":"30"},{"name":"windSensingStatus","value":"false"},{"name":"voiceSignStatus","value":"false"},{"name":"voiceStatus","value":"false"},{"name":"energySavingStatus","value":"false"},{"name":"lightStatus","value":"false"},{"name":"ch2oCleaningStatus","value":"false"},{"name":"pm2p5CleaningStatus","value":"false"},{"name":"humidificationStatus","value":"false"},{"name":"freshAirStatus","value":"false"},{"name":"windSpeed","value":"5"},{"name":"windDirectionVertical","value":"5"},{"name":"windDirectionHorizontal","value":"0"},{"name":"silentSleepStatus","value":"true"}]
 I (517149) uplug_info_get_cb: info_type = 1, status = 5
 op_name::grSetDAC
 attr_set:[{"name":"onOffStatus","value":"true"},{"name":"screenDisplayStatus","value":"true"},{"name":"echoStatus","value":"false"},{"name":"targetTemperature","value":"22.00"},{"name":"operationMode","value":"4"},{"name":"10degreeHeatingStatus","value":"false"},{"name":"muteStatus","value":"false"},{"name":"rapidMode","value":"false"},{"name":"healthMode","value":"false"},{"name":"humanSensingStatus","value":"0"},{"name":"selfCleaning56Status","value":"false"},{"name":"selfCleaningStatus","value":"false"},{"name":"cleaningTimeStatus","value":"false"},{"name":"cloudFilterChangeFlag","value":"false"},{"name":"specialMode","value":"0"},{"name":"energySavePeriod","value":"15"},{"name":"heatAccumulationStatus","value":"false"},{"name":"tempUnit","value":"1"},{"name":"pmvStatus","value":"false"},{"name":"intelligenceStatus","value":"false"},{"name":"halfDegreeSettingStatus","value":"false"},{"name":"lockStatus","value":"false"},{"name":"electricHeatingStatus","value":"false"},{"name":"targetHumidity","value":"30"},{"name":"windSensingStatus","value":"false"},{"name":"voiceSignStatus","value":"false"},{"name":"voiceStatus","value":"false"},{"name":"energySavingStatus","value":"false"},{"name":"lightStatus","value":"false"},{"name":"ch2oCleaningStatus","value":"false"},{"name":"pm2p5CleaningStatus","value":"false"},{"name":"humidificationStatus","value":"false"},{"name":"freshAirStatus","value":"false"},{"name":"windSpeed","value":"5"},{"name":"windDirectionVertical","value":"5"},{"name":"windDirectionHorizontal","value":"0"},{"name":"silentSleepStatus","value":"true"}]
 dev_set:{"subdeviceList":[]}
 INFO:exec info is 'onOffStatus true'
 INFO:exec info is 'screenDisplayStatus true'
 INFO:exec info is 'echoStatus false'
 INFO:exec info is 'targetTemperature 22.00'
 INFO:exec info is 'operationMode 4'
 INFO:exec info is '10degreeHeatingStatus false'
 INFO:exec info is 'muteStatus false'
 INFO:exec info is 'rapidMode false'
 INFO:exec info is 'healthMode false'
 INFO:exec info is 'humanSensingStatus 0'
 WRN:not found sub json in configfile:[selfCleaning56Status]
 INFO:exec info is 'selfCleaningStatus false'
 INFO:exec info is 'cleaningTimeStatus false'
 INFO:exec info is 'cloudFilterChangeFlag false'
 INFO:exec info is 'specialMode 0'
 INFO:exec info is 'energySavePeriod 15'
 WRN:not found sub json in configfile:[heatAccumulationStatus]
 INFO:exec info is 'tempUnit 1'
 INFO:exec info is 'pmvStatus false'
 INFO:exec info is 'intelligenceStatus false'
 INFO:exec info is 'halfDegreeSettingStatus false'
 INFO:exec info is 'lockStatus false'
 INFO:exec info is 'electricHeatingStatus false'
 INFO:exec info is 'targetHumidity 30'
 WRN:not found sub json in configfile:[windSensingStatus]
 WRN:not found sub json in configfile:[voiceSignStatus]
 WRN:not found sub json in configfile:[voiceStatus]
 INFO:exec info is 'energySavingStatus false'
 INFO:exec info is 'lightStatus false'
 INFO:exec info is 'ch2oCleaningStatus false'
 INFO:exec info is 'pm2p5CleaningStatus false'
 INFO:exec info is 'humidificationStatus false'
 INFO:exec info is 'freshAirStatus false'
 INFO:exec info is 'windSpeed 5'
 INFO:exec info is 'windDirectionVertical 5'
 INFO:exec info is 'windDirectionHorizontal 0'
 INFO:exec info is 'silentSleepStatus true'
 epp data: FF FF 14 00 00 00 00 00 00 01 60 01 06 06 85 00 02 21 00 00 00 00 2A
 I (517449) uplug_info_get_cb: wifi_ap_record.rssi = -49, wifi_info->rssi = 61
 I (517529) cdm_rx_task: s sFunc errors errors 00
 [UHomeOS][D][516882]: tx serial e++ data
 ff ff 0c 40 00 00 00 00 00 f7 00 00 00 3d 80 d5
 71
 [UHomeOS][I][516884]: [UART] tx [cmd 0xF7 len = 12, 17] ret 17
 I (517569) cdm_trigger_epp_request: pairs_count = 37, ret = 0
 [UHomeOS][D][516971]: rx serial e++ data
 ff ff 08 00 00 00 00 00 00 05 4d
 [UHomeOS][I][516972]: [UART] rx [cmd 0x05 len = 8]
 [UHomeOS][LOG][516972]: e++ comm time: 90
 [UHomeOS][D][517041]: tx serial e++ data
 ff ff 14 40 00 00 00 00 00 01 60 01 06 06 85 00
 02 21 00 00 00 00 6a 20 1e
 [UHomeOS][I][517043]: [UART] tx [cmd 0x01 len = 20, 25] ret 25
 [UHomeOS][D][517171]: rx serial e++ data
 ff ff 2a 00 00 00 00 00 00 02 6d 01 06 06 85 00
 02 21 00 00 00 00 34 00 5f 00 00 03 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 24
 [UHomeOS][I][517176]: [UART] rx [cmd 0x02 len = 42]
 [UHomeOS][LOG][517181]: e++ comm time: 140
 I (518089) cdm_rx_task: s sFunc errors errors 00
 [UHomeOS][I][517551]: status changed
 uplus_get_pkt_buf_list called
 [UHomeOS][D][517571]: rsp
 ff ff 2a 00 00 00 00 00 00 02 6d 01 06 06 85 00
 02 21 00 00 00 00 34 00 5f 00 00 03 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 e4
 ERR:e++ data[0][type:2]doesn't match with attr[errCode]'s value list in configfile
 [UHomeOS][W][517618]: rx op rsp, sn 18, result 0
 op response, sn 18, result 0, pairs 49
 I (518269) cdm_command_task: cdm_command_executed_queue_element = SUCCESS
 uplus_get_pkt_buf_list called
 [UHomeOS][D][517675]: rpt
 ff ff 2a 00 00 00 00 00 00 06 6d 01 06 06 85 00
 02 21 00 00 00 00 34 00 5f 00 00 03 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 e8
 ERR:e++ data[0][type:2]doesn't match with attr[errCode]'s value list in configfile
 status report pairs 49
 I (518369) cdm_enqueue_rx: free heap 59948
 I (518379) cdm_enqueue_rx: free heap 57068
 I (518569) uplus_epp_usr_monitor: e_EPP_Protocol_Connection_State = 10, status = 5, free heap = 70568
 I (518599) cdm_rx_task: 1 r targetTemperature = 22.00, tempSel = 22.00, cTab 0 0, cFunc 0  0
 I (518609) cdm_rx_task: 1 r windDirectionVertical = 5, windDirectionVertical = 5, cTab 0 0, cFunc 0  0
 I (518619) cdm_rx_task: 1 r operationMode = 4, machMode = 4, cTab 0 0, cFunc 0  0
 I (518629) cdm_rx_task: 1 r specialMode = 0, specialMode = 0, cTab 0 0, cFunc 0  0
 I (518649) cdm_rx_task: 1 r windSpeed = 5, windSpeed = 5, cTab 0 0, cFunc 0  0
 I (518649) cdm_rx_task: 1 r energySavePeriod = 15, energySavePeriod = 15, cTab 0 0, cFunc 0  0
 I (518669) cdm_rx_task: 1 r tempUnit = 1, tempUnit = 0, cTab 1 1, cFunc 0  0
 I (518679) cdm_rx_task: 1 r pmvStatus = false, pmvStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518689) cdm_rx_task: 1 r intelligenceStatus = false, intelligenceStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518699) cdm_rx_task: 1 r halfDegreeSettingStatus = false, halfDegreeSettingStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518719) cdm_rx_task: 1 r screenDisplayStatus = true, screenDisplayStatus = 1, cTab 0 0, cFunc 1 ft01 1
 I (518719) cdm_rx_task: 1 r 10degreeHeatingStatus = false, 10degreeHeatingStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518739) cdm_rx_task: 1 r echoStatus = false, echoStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518759) cdm_rx_task: 1 r lockStatus = false, lockStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518769) cdm_rx_task: 1 r silentSleepStatus = true, silentSleepStatus = 1, cTab 0 0, cFunc 1 ft01 1
 I (518779) cdm_rx_task: 1 r muteStatus = false, muteStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518799) cdm_rx_task: 1 r rapidMode = false, rapidMode = 0, cTab 0 0, cFunc 1 ft01 1
 I (518799) cdm_rx_task: 1 r electricHeatingStatus = false, electricHeatingStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518819) cdm_rx_task: 1 r healthMode = false, healthMode = 0, cTab 0 0, cFunc 1 ft01 1
 I (518829) cdm_rx_task: 1 r onOffStatus = true, onOffStatus = 1, cTab 0 0, cFunc 1 ft01 1
 I (518849) cdm_rx_task: 1 r targetHumidity = 30, humiditySel = 30, cTab 0 0, cFunc 0  0
 I (518859) cdm_rx_task: 1 r humanSensingStatus = 0, humanSensingStatus = 0, cTab 0 0, cFunc 0  0
 I (518869) cdm_rx_task: 1 r windDirectionHorizontal = 0, windDirectionHorizontal = 0, cTab 0 0, cFunc 0  0
 I (518879) cdm_rx_task: 1 r localFilterChangeFlag = false, filterChangeStatusLocal = 0, cTab 0 0, cFunc 1 ft01 1
 I (518889) cdm_rx_task: 1 r energySavingStatus = false, energySavingStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518909) cdm_rx_task: 1 r lightStatus = false, lightStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518919) cdm_rx_task: 1 r selfCleaningStatus = false, selfCleaningStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518929) cdm_rx_task: 1 r ch2oCleaningStatus = false, ch2oCleaningStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518949) cdm_rx_task: 1 r pm2p5CleaningStatus = false, pm2p5CleaningStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518959) cdm_rx_task: 1 r humidificationStatus = false, humidificationStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518969) cdm_rx_task: 1 r freshAirStatus = false, freshAirStatus = 0, cTab 0 0, cFunc 1 ft01 1
 I (518989) cdm_rx_task: 1 r indoorTemperature = 26.00, tempIndoor = 26.00, cTab 0 0, cFunc 0  0
 I (518999) cdm_rx_task: 1 r indoorHumidity = 0, humidityIndoor = 0, cTab 0 0, cFunc 0  0
 I (519009) cdm_rx_task: 1 r outdoorTemperature = 31.00, tempOutdoor = 31.00, cTab 0 0, cFunc 0  0
 I (519019) cdm_rx_task: 1 r acType = 0, acType = 0, cTab 0 0, cFunc 0  0
 I (519039) cdm_rx_task: 1 r sensingResult = 0, sensingResult = 0, cTab 0 0, cFunc 0  0
 I (519039) cdm_rx_task: 1 r airQuality = 0, airQuality = 0, cTab 0 0, cFunc 0  0
 I (519059) cdm_rx_task: 1 r pm2p5Level = 0, pm2p5LevelIndoor = 0, cTab 0 0, cFunc 0  0
 I (519069) cdm_rx_task: 1 r errCode = , errCode = , cTab 0 0, cFunc 0  0
 I (519079) cdm_rx_task: 1 r ErrAckFlag = false, errAckFlag = 0, cTab 0 0, cFunc 1 ft01 1
 I (519099) cdm_rx_task: 1 r operationModeHK = 0, machineModeHK = 0, cTab 0 0, cFunc 0  0
 I (519109) cdm_rx_task: 1 r opSrc = 3, operationSource = 3, cTab 0 0, cFunc 0  0
 I (519129) cdm_rx_task: 1 r totalCleaningTime = 0, totalWorkTime = 0, cTab 0 0, cFunc 0  0
 I (519129) cdm_rx_task: 1 r indoorPM2p5Value = 0, pm2p5ValueIndoor = 0, cTab 0 0, cFunc 0  0
 I (519149) cdm_rx_task: 1 r outdoorPM2p5Value = 0, pm2p5ValueOutdoor = 0, cTab 0 0, cFunc 0  0
 I (519149) cdm_rx_task: 1 r ch2oValue = 0, ch2oValueIndoor = 0, cTab 0 0, cFunc 0  0
 I (519179) cdm_rx_task: 1 r vocValue = 0, vocValueIndoor = 0, cTab 0 0, cFunc 0  0
 I (519179) cdm_rx_task: 1 r co2Value = 0, co2ValueIndoor = 0, cTab 0 0, cFunc 0  0
 I (519199) cdm_rx_task: 1 r totalElectricityUsed = 0, totalElectricityUsed = 0, cTab 0 0, cFunc 0  0
 I (519239) cdm_rx_task: s sFunc errors errors 00
 I (519249) cdm_rx_task: + silentSleepStatus = 0 -> 1, isSuperTriggerStatusChange 1
 I (519249) cdm_rx_task: isSuperTriggerStatusChange = 1, isStatusChange = 1, isTransmissionRateElapsed = 0, currentTimestamp = 1628677172, transmissionTimestamp = 1628677170, elapsed = 2, transMode = 0, transmissionRate = 3600
 [UHomeOS][D][518672]: tx serial e++ data
 ff ff 08 40 00 00 00 00 00 73 bb 87 01
 [UHomeOS][I][518674]: [UART] tx [cmd 0x73 len = 8, 13] ret 13
 [UHomeOS][D][518761]: rx serial e++ data
 ff ff 12 00 00 00 00 00 00 74 0f 5a 00 00 00 00
 00 00 00 00 2f
 [UHomeOS][I][518762]: [UART] rx [cmd 0x74 len = 18]
 [UHomeOS][LOG][518765]: e++ comm time: 94
 [UHomeOS][D][518851]: tx serial e++ data
 ff ff 0a 40 00 00 00 00 00 01 4d 01 99 b3 b4
 [UHomeOS][I][518852]: [UART] tx [cmd 0x01 len = 10, 15] ret 15
 I (519579) uplus_epp_usr_monitor: e_EPP_Protocol_Connection_State = 10, status = 5, free heap = 71392
 [UHomeOS][D][518971]: rx serial e++ data
 ff ff 2a 00 00 00 00 00 00 02 6d 01 06 06 85 00
 02 21 00 00 00 00 34 00 5f 00 00 03 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 24
 [UHomeOS][I][518977]: [UART] rx [cmd 0x02 len = 42]
 [UHomeOS][LOG][518981]: e++ comm time: 130
 [UHomeOS][D][519062]: tx serial e++ data
 ff ff 08 40 00 00 00 00 00 fc 44 23 40
 [UHomeOS][I][519062]: [UART] tx [cmd 0xFC len = 8, 13] ret 13
 I (519819) cdm_rx_task: s sFunc errors errors 00
 [UHomeOS][D][519163]: rx serial e++ data
 ff ff 0e 00 00 00 00 00 00 fd 00 00 00 00 00 00
 4b
 [UHomeOS][I][519165]: [UART] rx [cmd 0xFD len = 14]
 [UHomeOS][LOG][519169]: e++ comm time: 108

2 Likes

Mabe is a Mexican division of Haier, and from pictures I saw of the “Mabe” wifi card, I realized they were both using the same KZW-W001. I just used your code and was able to add my Mabe branded mini split to Home Assistant with little issue. The only thing is when I select Cool in the Home Assistant app, the mini split display shows a green snowflake/green star of some sort, and not the blue snowflake for cooling mode. Everything else works perfect.

Maybe enable “eco mode”?

If you set the cool mode from device remote, with show Home Assistant?

I ordered JST XH connectors but the won’t fit.
They are way to big!

So I went through and reflashed the Wemos and saw all the Deprecated comments on the swing and modes, so I went through the forks of your code.

I found someone, Okison, that had revised your code to incorporate swing and the new Home Assistant code.

Revised Haier.h code incorporating Swing by Okison

The previous issue with the green snowflake disappeared with flashing Okison’s version, but now, the Home Assistant climate page for the mini split shows “Off” regardless of mode. Everything works, but the persistent “Off” is confusing.

1 Like

Because I still doesn’t have the right connector i put the Wemos D1 at the ESP32.
With the Haierv2.h file from albetaCOM GitHub - albetaCOM/esp-haier: ESP8266 code to connect to Haier Air Conditioner ((firmware version I can communicate with the unit.

It can turn it on and off. When I set temperature it goes to “Health” mode (the same as fresh or purified?). When I set another mode it also goes to health mode.
It turns on when I select a mode like “cool or heat” but it goes on “Health”

I don’t have the knowhow from here. But changing the ESP32 for a ESP8266 gives opportunities.

A little late to the party, but did you check, if your unit is a Midea one? There would be a solution ready to use. It looks a lot like the Midea PCBs…

Check here: Midea branded AC’s with ESPhome (no cloud)

Try to delete or comment row 123:

traits.set_supports_action(true);

I’m using the Haierv2.h file from albetaCOM GitHub - albetaCOM/esp-haier:

I’ve modified Haierv2.h in order to eliminate deprecated warnings and I had your same problem.
For me solution has been to delete

traits.set_supports_action(true);

I’ve a workaround for the “Health” problem.
Below is a new version of Haierv2.h file.
Now is Haierv3.h
The Health problem seems to be related to the Purify flag.

If PurifyControl is set to false (row 471 of Haierv3.h ) the Purify status is toggle at every command
(don’t ask why)
So the solution is to send the message two times with an interval of 1 sec.
Look at row 759

  • Result is that the Purify status after the second message is maintained (if was ON remain ON, if was off remain OFF)

  • You can change the Purify using the remote.

  • Not a perfect solution, just a workaround :slight_smile:

I’ve also removed all the ‘deprecated’ messages.

Here is the modified file

/**
* Create by Miguel Ángel López on 20/07/19
* Modified by Alba Prades on 21/07/20
* Modified by Alba Prades on 13/08/20 
* Modified by Alba Prades on 25/08/20: Added fan, dry and swing features
*      Added modes
* Modified by Carlo Pinasco on 25/08/21: Workaround to fix Purify problem and fix deprecated warning messages.
*   
*   If PurifyControl is set to false (row 471) the Purify status is toggle at every command
*   (don't ask why)
*   So the solution is to send the message two times with an interval of 1 sec.
*   Look at row 759
*   Result is that the Purify status after the second message is maintened (if was ON remain ON, if was off remain OFF)
*   You can change the Purify using the remote.
*   Not a perfect solution, just a workaround :-)
*
*
**/

#ifndef HAIER_ESP_HAIER_H
#define HAIER_ESP_HAIER_H

#include "esphome.h"
#include <string>

using namespace esphome;
using namespace esphome::climate;

// Updated read offset

#define MODE_OFFSET 			14
#define MODE_MSK				0xF0
	#define MODE_AUTO       	0x00
	#define MODE_DRY			0x40
	#define MODE_COOL			0x20
	#define MODE_HEAT			0x80
	#define MODE_FAN			0xC0
#define FAN_MSK					0x0F
	#define FAN_LOW	    		0x03
	#define FAN_MID		  		0x02
	#define FAN_HIGH	     	0x01
	#define FAN_AUTO	   		0x05
	
#define HORIZONTAL_SWING_OFFSET		19
	#define HORIZONTAL_SWING_CENTER 		0x00
	#define HORIZONTAL_SWING_MAX_LEFT 		0x03
	#define HORIZONTAL_SWING_LEFT 			0x04
	#define HORIZONTAL_SWING_MAX_RIGHT 		0x06
	#define HORIZONTAL_SWING_RIGHT 			0x05
	#define HORIZONTAL_SWING_AUTO 			0x07
	
#define VERTICAL_SWING_OFFSET			13
	#define VERTICAL_SWING_MAX_UP			0x02
	#define VERTICAL_SWING_UP				0x04
	#define VERTICAL_SWING_CENTER				0x06
	#define VERTICAL_SWING_DOWN				0x08
	#define VERTICAL_SWING_HEALTH_UP			0x01
	#define VERTICAL_SWING_HEALTH_DOWN		0x03
	#define VERTICAL_SWING_AUTO 				0x0C

#define TEMPERATURE_OFFSET   	22

#define STATUS_DATA_OFFSET			17 // Purify/Quiet mode/OnOff/...
	#define POWER_BIT				(0)	
	#define PURIFY_BIT				(1)	
	#define QUIET_BIT				(3)	
	#define AUTO_FAN_MAX_BIT		(4)
	
#define SET_POINT_OFFSET 		12	

// Another byte
	#define SWING        		27
	#define SWING_OFF          	0
	#define SWING_VERTICAL     	1
	#define SWING_HORIZONTAL   	2
	#define SWING_BOTH

	#define LOCK        		28
	#define LOCK_ON     		80
	#define LOCK_OFF    		00

// Updated read offset



#define FRESH       			31
	#define FRESH_ON    		1
	#define FRESH_OFF   		0

// Updated read offset


#define COMMAND_OFFSET			9
	#define RESPONSE_POLL		2

#define CRC_OFFSET(message)		(2 + message[2])

// Control commands
#define CTR_POWER_OFFSET		13
	#define CTR_POWER_ON		0x01
	#define CTR_POWER_OFF		0x00
	
#define POLY 0xa001


// temperatures supported by AC system
#define MIN_SET_TEMPERATURE 16
#define MAX_SET_TEMPERATURE 30

//if internal temperature is outside of those boundaries, message will be discarded
#define MIN_VALID_INTERNAL_TEMP 10
#define MAX_VALID_INTERNAL_TEMP 50

class Haier : public Climate, public PollingComponent {

private:

    byte lastCRC;
    byte status[47];
	
	byte initialization_1[13] = {0xFF,0xFF,0x0A,0x0,0x0,0x0,0x0,0x0,0x00,0x61,0x00,0x07,0x72};
	byte initialization_2[13] = {0xFF,0xFF,0x08,0x40,0x0,0x0,0x0,0x0,0x0,0x70,0xB8,0x86,0x41};
 	byte poll[15] = {0xFF,0xFF,0x0A,0x40,0x00,0x00,0x00,0x00,0x00,0x01,0x4D,0x01,0x99,0xB3,0xB4};
    byte power_command[17]     = {0xFF,0xFF,0x0C,0x40,0x00,0x00,0x00,0x00,0x00,0x01,0x5D,0x01,0x00,0x01,0xAC,0xBD,0xFB};
	byte control_command[25] = {0xFF,0xFF,0x14,0x40,0x00,0x00,0x00,0x00,0x00,0x01,0x60,0x01,0x09,0x08,0x25,0x00,0x02,0x03,0x00,0x06,0x00,0x0C,0x03,0x0B,0x70};

	byte climate_mode_fan_speed = FAN_AUTO;
	byte climate_mode_setpoint = 0x0A;
	
	byte fan_mode_fan_speed = FAN_HIGH;
	byte fan_mode_setpoint = 0x08;
	
	bool first_status_received = false;
	
	// Some vars for debuging purposes	
	byte previous_status[47];
	bool previous_status_init = false;
	
	
	// Functions

	void SetHvacModeControl(byte mode)
	{
		control_command[MODE_OFFSET] &= ~MODE_MSK;
		control_command[MODE_OFFSET] |= mode;
	}	
	
	byte GetHvacModeStatus()
	{
		return status[MODE_OFFSET] & MODE_MSK;
	}	
	
	void SetTemperatureSetpointControl(byte temp)
	{ 
		control_command[SET_POINT_OFFSET] = temp;
	}	
	
	byte GetTemperatureSetpointStatus()
	{
		return status[SET_POINT_OFFSET];
	}	
	
	void SetFanSpeedControl(byte fan_mode)
	{
		control_command[MODE_OFFSET] &= ~FAN_MSK;
		control_command[MODE_OFFSET] |= fan_mode;
	}
	
	byte GetFanSpeedStatus()
	{
		return status[MODE_OFFSET] & FAN_MSK;
	}		
	
	void SetHorizontalSwingControl(byte swing_mode)
	{
		control_command[HORIZONTAL_SWING_OFFSET] = swing_mode;
	}
	
	byte GetHorizontalSwingStatus()
	{
		return status[HORIZONTAL_SWING_OFFSET];
	}	
	
	void SetVerticalSwingControl(byte swing_mode)
	{
		control_command[VERTICAL_SWING_OFFSET] = swing_mode;
	}
	
	byte GetVerticalSwingStatus()
	{
		return status[VERTICAL_SWING_OFFSET];
	}	
	
	void SetQuietModeControl(bool quiet_mode)
	{
		byte tmp;
		byte msk;
		
		msk = (0x01 << QUIET_BIT);		
		
		if(quiet_mode == true){
			control_command[STATUS_DATA_OFFSET] |= msk;
		}
		else{
			msk = ~msk;
			control_command[STATUS_DATA_OFFSET] &= msk;
		}
	}
	
	bool GetQuietModeStatus( void )
	{
		bool ret = false;		
		byte tmp;
		byte msk;
		
		msk = (0x01 << QUIET_BIT);
		tmp = status[STATUS_DATA_OFFSET] & msk;
		
		if(tmp != 0) ret = true;
		
		return ret;
	}
	
	
	void SetPurifyControl(bool purify_mode)
	{
		byte tmp;
		byte msk;
		
		msk = (0x01 << PURIFY_BIT);		
		
		if(purify_mode == true){
			control_command[STATUS_DATA_OFFSET] |= msk;
		}
		else{
			msk = ~msk;
			control_command[STATUS_DATA_OFFSET] &= msk;
		}
	}
	
	bool GetPurifyStatus( void )
	{
		bool ret = false;		
		byte tmp;
		byte msk;
		
		msk = (0x01 << PURIFY_BIT);
		tmp = status[STATUS_DATA_OFFSET] & msk;
		
		if(tmp != 0) ret = true;
		
		return ret;
	}
	
	void SetPowerControl(bool power_mode)
	{
		byte tmp;
		byte msk;
		
		msk = (0x01 << POWER_BIT);		
		
		if(power_mode == true){
			control_command[STATUS_DATA_OFFSET] |= msk;
		}
		else{
			msk = ~msk;
			control_command[STATUS_DATA_OFFSET] &= msk;
		}
	}
	
	bool GetPowerStatus( void )
	{
		bool ret = false;		
		byte tmp;
		byte msk;
		
		msk = (0x01 << POWER_BIT);
		tmp = status[STATUS_DATA_OFFSET] & msk;
		
		if(tmp != 0) ret = true;
		
		return ret;
	}
	
	
	bool GetFastModeStatus( void )
	{
		bool ret = false;		
		byte tmp;
		byte msk;
		
		msk = (0x01 << AUTO_FAN_MAX_BIT);
		tmp = status[STATUS_DATA_OFFSET] & msk;
		
		if(tmp != 0) ret = true;
		
		return ret;
	}
	
	void SetFastModeControl(bool fast_mode)
	{
		byte tmp;
		byte msk;
		
		msk = (0x01 << AUTO_FAN_MAX_BIT);		
		
		if(fast_mode == true){
			control_command[STATUS_DATA_OFFSET] |= msk;
		}
		else{
			msk = ~msk;
			control_command[STATUS_DATA_OFFSET] &= msk;
		}
	}
	
	
	void CompareStatusByte()
	{
		int i;
		
		if(previous_status_init == false){
			for (i=0;i<sizeof(status);i++){
				previous_status[i] = status[i];
			}
			previous_status_init = true;
		}
		
		for (i=0;i<sizeof(status);i++)
		{
			if(status[i] != previous_status[i]){
				ESP_LOGD("Debug", "Status byte %d: 0x%X --> 0x%X ", i, previous_status[i],status[i]);
			}
			previous_status[i] = status[i];
		}
	}


public:

    Haier() : PollingComponent(5 * 1000) {
        lastCRC = 0;
    }


    
    void setup() override {
        
        Serial.begin(9600);
		delay(1000);
		Serial.write(initialization_1, sizeof(initialization_1));
        auto raw = getHex(initialization_1, sizeof(initialization_1));
        ESP_LOGD("Haier", "initialization_1: %s ", raw.c_str());
		delay(1000);
		Serial.write(initialization_2, sizeof(initialization_2));
        raw = getHex(initialization_2, sizeof(initialization_2));
        ESP_LOGD("Haier", "initialization_2: %s ", raw.c_str());
    }

    void loop() override  {
		byte data[47];
        if (Serial.available() > 0) {
			if (Serial.read() != 255) return;
			if (Serial.read() != 255) return;
			
			data[0] = 255;
			data[1] = 255;

            Serial.readBytes(data+2, sizeof(data)-2);
			
			// If is a status response
			if (data[COMMAND_OFFSET] == RESPONSE_POLL) {
				// Update the status frame
				memcpy(status, data, sizeof(status));
				parseStatus();
			}
		}
    }

    void update() override {
        
        Serial.write(poll, sizeof(poll));
        auto raw = getHex(poll, sizeof(poll));
        ESP_LOGD("Haier", "POLL: %s ", raw.c_str());
    }

protected:
    ClimateTraits traits() override {
        auto traits = climate::ClimateTraits();
        //traits.set_supports_away(false);
        //traits.set_supports_auto_mode(true);
        //traits.set_supports_heat_mode(true);
        //traits.set_supports_cool_mode(true);
        //traits.set_supports_dry_mode(true);
        //traits.set_supports_fan_only_mode(true);
        //traits.set_supports_fan_mode_on(false);
        //traits.set_supports_fan_mode_off(false);
        //traits.set_supports_fan_mode_auto(true);
        //traits.set_supports_fan_mode_low(true);
        //traits.set_supports_fan_mode_medium(true);
        //traits.set_supports_fan_mode_middle(true);
        //traits.set_supports_fan_mode_high(true);
       // traits.set_supports_fan_mode_focus(false);
        //traits.set_supports_fan_mode_diffuse(false);		
        
        //traits.set_supports_action(true);

        traits.set_supported_modes(
        {
            climate::CLIMATE_MODE_OFF,
            climate::CLIMATE_MODE_COOL,
            climate::CLIMATE_MODE_HEAT,
            climate::CLIMATE_MODE_FAN_ONLY,
            climate::CLIMATE_MODE_DRY,
            climate::CLIMATE_MODE_AUTO
        });

        traits.set_supported_fan_modes(
        {
            climate::CLIMATE_FAN_AUTO,
            climate::CLIMATE_FAN_LOW,
            climate::CLIMATE_FAN_MEDIUM,
            climate::CLIMATE_FAN_HIGH,
            climate::CLIMATE_FAN_MIDDLE
            
        });

        traits.set_supported_swing_modes(
        {
            climate::CLIMATE_SWING_OFF,
            climate::CLIMATE_SWING_BOTH,
            climate::CLIMATE_SWING_VERTICAL,
            climate::CLIMATE_SWING_HORIZONTAL
        });

        traits.set_visual_min_temperature(MIN_SET_TEMPERATURE);
        traits.set_visual_max_temperature(MAX_SET_TEMPERATURE);
        traits.set_visual_temperature_step(1.0f);
        traits.set_supports_current_temperature(true);
        //traits.set_supports_swing_mode_off(true);
        //traits.set_supports_swing_mode_both(true);
        //traits.set_supports_swing_mode_vertical(true);
        //traits.set_supports_swing_mode_horizontal(true);		
        //traits.set_supports_action(true);// Cal identificar el byte
        return traits;
    }

public:

    void parseStatus() {


        auto raw = getHex(status, sizeof(status));
        ESP_LOGD("Haier", "Readed message ALBA: %s ", raw.c_str());

        byte check = getChecksum(status, sizeof(status));

        if (check != status[CRC_OFFSET(status)]) {
            ESP_LOGW("Haier", "Invalid checksum (%d vs %d)", check, status[CRC_OFFSET(status)]);
            return;
        }

        lastCRC = check;

        current_temperature = status[TEMPERATURE_OFFSET]/2;
        target_temperature = status[SET_POINT_OFFSET] + 16;

        if(current_temperature < MIN_VALID_INTERNAL_TEMP || current_temperature > MAX_VALID_INTERNAL_TEMP 
            || target_temperature < MIN_SET_TEMPERATURE || target_temperature > MAX_SET_TEMPERATURE){
            ESP_LOGW("Haier", "Invalid temperatures");
            return;
        }
		
		// Read all the info from the status message and update values in control message
		// so the next message is updated
		// This is usefull if there are manual changes with the remote control
		SetPowerControl(GetPowerStatus());
		SetHvacModeControl(GetHvacModeStatus());
		// workaround for Purify problem
        SetPurifyControl(false);
		SetQuietModeControl(GetQuietModeStatus());
		SetFastModeControl(GetFastModeStatus());
		SetFanSpeedControl(GetFanSpeedStatus());
		SetHorizontalSwingControl(GetHorizontalSwingStatus());
		SetVerticalSwingControl(GetVerticalSwingStatus());
		SetTemperatureSetpointControl(GetTemperatureSetpointStatus());
		
		if(GetHvacModeStatus() == MODE_FAN){
			fan_mode_fan_speed = GetFanSpeedStatus();
			fan_mode_setpoint = GetTemperatureSetpointStatus();
		}
		else{
			climate_mode_fan_speed = GetFanSpeedStatus();
			climate_mode_setpoint = GetTemperatureSetpointStatus();
		}
		
		// Flag to enable modifications from UI as we now know the status of the A/C
		first_status_received = true;

		
		// DEBUG DATA, uncomment what's needed
		//ESP_LOGW("Debug", "Power Status = 0x%X", GetPowerStatus());
		//ESP_LOGW("Debug", "HVAC Mode = 0x%X", GetHvacModeStatus());
		//ESP_LOGW("Debug", "Purify status = 0x%X", GetPurifyStatus());
		//ESP_LOGW("Debug", "Quiet mode Status = 0x%X", GetQuietModeStatus());
		//ESP_LOGW("Debug", "Fast mode Status = 0x%X", GetFastModeStatus());
		//ESP_LOGW("Debug", "Fan speed Status = 0x%X", GetFanSpeedStatus());
		//ESP_LOGW("Debug", "Horizontal Swing Status = 0x%X", GetHorizontalSwingStatus());
		//ESP_LOGW("Debug", "Vertical Swing Status = 0x%X", GetVerticalSwingStatus());
		//ESP_LOGW("Debug", "Set Point Status = 0x%X", GetTemperatureSetpointStatus());
		CompareStatusByte();
		
		
		// Update home assistant component
		
        if (GetPowerStatus() == false) {
            mode = CLIMATE_MODE_OFF;
		} else {
			// Check current hvac mode
            switch (GetHvacModeStatus()) {
                case MODE_COOL:
                    mode = CLIMATE_MODE_COOL;
                    break;
                case MODE_HEAT:
                    mode = CLIMATE_MODE_HEAT;
                    break;
                case MODE_DRY:
				    mode = CLIMATE_MODE_DRY;
					break;
				case MODE_FAN:
                    mode = CLIMATE_MODE_FAN_ONLY;
                    break;
                case MODE_AUTO:
                default:
                    mode = CLIMATE_MODE_AUTO;
            }
					
			// Get fan speed
			// If "quiet mode" is set we will read it as "fan low"
			if ( GetQuietModeStatus() == true) {
                fan_mode = CLIMATE_FAN_LOW;
            }
			// If we detect that fast mode is on the we read it as "fan high"
			else if( GetFastModeStatus() == true) {
				fan_mode = CLIMATE_FAN_HIGH;
			}			
			else {				
				// No quiet or fast so we read the actual fan speed.
                switch (GetFanSpeedStatus()) {
                    case FAN_AUTO:
                        fan_mode = CLIMATE_FAN_AUTO;
                        break;
                    case FAN_MID:
                        fan_mode = CLIMATE_FAN_MEDIUM;
                        break;
                    //case FAN_MIDDLE:
                    //    fan_mode = CLIMATE_FAN_MIDDLE;
                    //    break;
					case FAN_LOW:
						fan_mode = CLIMATE_FAN_LOW;
                        break;
                    case FAN_HIGH:
                        fan_mode = CLIMATE_FAN_HIGH;
                        break;
                    default:
                        fan_mode = CLIMATE_FAN_AUTO;
						
                }
            }				


			// Check the status of the swings (vertical and horizontal and translate according component configuration
			if( (GetHorizontalSwingStatus() == HORIZONTAL_SWING_AUTO) && (GetVerticalSwingStatus() == VERTICAL_SWING_AUTO) ){
				swing_mode = CLIMATE_SWING_BOTH;				
			}
			else if(GetHorizontalSwingStatus() == HORIZONTAL_SWING_AUTO){
				swing_mode = CLIMATE_SWING_HORIZONTAL;
			}
			else if(GetVerticalSwingStatus() == VERTICAL_SWING_AUTO){
				swing_mode = CLIMATE_SWING_VERTICAL;
			}
			else{
				swing_mode = CLIMATE_SWING_OFF;
			}
		}

        this->publish_state();

    }


    void control(const ClimateCall &call) override {
		
        ClimateMode new_mode;
		bool new_control_cmd = false;
		
		
		ESP_LOGD("Control", "Control call");
		
		if(first_status_received == false){
			ESP_LOGD("Control", "No action, first poll answer not received");
			return;
		}

        if (call.get_mode().has_value()) {
            // User requested mode change
            new_mode = *call.get_mode();
        
			ESP_LOGD("Control", "*call.get_mode() = %d", new_mode);
			
			// It seems that this message is no needed, we keep it here commented
						
			if((new_mode != CLIMATE_MODE_OFF) && (GetPowerStatus() == false)){
			//	 if the current mode is off -> we need to power on
				sendData(power_command, sizeof(power_command));  
				delay(1000);	
			}
			else 
			{
			
            switch (new_mode) {
                case CLIMATE_MODE_OFF:
					SetPowerControl(false);
					sendData(control_command, sizeof(control_command)); 
                    break;
					
                case CLIMATE_MODE_AUTO:
					SetPowerControl(true);
					SetHvacModeControl(MODE_AUTO);
					
					// Recover fan_speed and setpoint (when switching to fan_only they are "lost")
					SetFanSpeedControl(climate_mode_fan_speed);
					SetTemperatureSetpointControl(climate_mode_setpoint);	
						
					sendData(control_command, sizeof(control_command));
                    break;
					
                case CLIMATE_MODE_HEAT:	
					SetPowerControl(true);
					SetHvacModeControl(MODE_HEAT);
					
					// Recover fan_speed and setpoint (when switching to fan_only they are "lost")
					SetFanSpeedControl(climate_mode_fan_speed);
					SetTemperatureSetpointControl(climate_mode_setpoint);	
					
					sendData(control_command, sizeof(control_command));
                    break;
					
                case CLIMATE_MODE_DRY:		
					SetPowerControl(true);
					SetHvacModeControl(MODE_DRY);
					
					// Recover fan_speed and setpoint (when switching to fan_only they are "lost")
					SetFanSpeedControl(climate_mode_fan_speed);
					SetTemperatureSetpointControl(climate_mode_setpoint);	
										
					sendData(control_command, sizeof(control_command));					
                    break;
					
                case CLIMATE_MODE_FAN_ONLY:				
					SetPowerControl(true);
					SetHvacModeControl(MODE_FAN);				
					
					// Recover fan_speed and setpoint (fan_only values are "special")
					SetFanSpeedControl(fan_mode_fan_speed);
					SetTemperatureSetpointControl(fan_mode_setpoint);	
					
					sendData(control_command, sizeof(control_command));
                    break;

                case CLIMATE_MODE_COOL:
					SetPowerControl(true);
					SetHvacModeControl(MODE_COOL);
					
					// Recover fan_speed and setpoint (when switching to fan_only they are "lost")
					SetFanSpeedControl(climate_mode_fan_speed);
					SetTemperatureSetpointControl(climate_mode_setpoint);
					
					sendData(control_command, sizeof(control_command));
                    break;
            }
			}
            // Publish updated state
            mode = new_mode;
            this->publish_state();
		}
		
		        //Set fan speed
        if (call.get_fan_mode().has_value()) {
            switch(call.get_fan_mode().value()) {
                case CLIMATE_FAN_LOW:
					SetFanSpeedControl(FAN_LOW);
                    break;
                case CLIMATE_FAN_MIDDLE:
					SetFanSpeedControl(FAN_MID);
                    break;
                case CLIMATE_FAN_MEDIUM:
					SetFanSpeedControl(FAN_MID);
                    break;
                case CLIMATE_FAN_HIGH:
					SetFanSpeedControl(FAN_HIGH);
                    break;
                case CLIMATE_FAN_AUTO:
                    SetFanSpeedControl(FAN_AUTO);
                    break;
			}
			sendData(control_command, sizeof(control_command)); 
		}

        //Set swing mode
        if (call.get_swing_mode().has_value()){
            switch(call.get_swing_mode().value()) {
                case CLIMATE_SWING_OFF:
					// When not auto we decide to set it to the center
					SetHorizontalSwingControl(HORIZONTAL_SWING_CENTER);
					// When not auto we decide to set it to the center
					SetVerticalSwingControl(VERTICAL_SWING_CENTER);
                    break;
                case CLIMATE_SWING_VERTICAL:
					// When not auto we decide to set it to the center
                    SetHorizontalSwingControl(HORIZONTAL_SWING_CENTER);
					SetVerticalSwingControl(VERTICAL_SWING_AUTO);
                    break;
                case CLIMATE_SWING_HORIZONTAL:
                    SetHorizontalSwingControl(HORIZONTAL_SWING_AUTO);
					// When not auto we decide to set it to the center
					SetVerticalSwingControl(VERTICAL_SWING_CENTER);
                    break;
                case CLIMATE_SWING_BOTH:
                    SetHorizontalSwingControl(HORIZONTAL_SWING_AUTO);
					SetVerticalSwingControl(VERTICAL_SWING_AUTO);
                    break;
			}
			sendData(control_command, sizeof(control_command)); 
        }
		
		
		if (call.get_target_temperature().has_value()) {
		    float temp = *call.get_target_temperature();
			ESP_LOGD("Control", "*call.get_target_temperature() = %f", temp);
			control_command[SET_POINT_OFFSET] = (unsigned int) temp - 16;
			sendData(control_command, sizeof(control_command));			
			target_temperature = temp;
            this->publish_state();
		}
		
		
   }


    void sendData(byte * message, byte size) {
        byte crc_offset = CRC_OFFSET(message);
        byte crc = getChecksum(message, size);
        word crc_16 = crc16(0, &(message[2]), crc_offset-2);
        
        // Updates the crc
        message[crc_offset] = crc;
        message[crc_offset+1] = (crc_16>>8)&0xFF;
        message[crc_offset+2] = crc_16&0xFF;

        Serial.write(message, size); delay(1000);Serial.write(message, size);
        auto raw = getHex(message, size);
        ESP_LOGD("Haier", "Message sent: %s  - CRC: %X - CRC16: %X", raw.c_str(), crc, crc_16);

    }

    String getHex(byte * message, byte size) {


        String raw;

        for (int i=0; i < size; i++){
			raw += " " + String(message[i]);

        }
        raw.toUpperCase();

        return raw;


    }

    byte getChecksum(const byte * message, size_t size) {
		byte position = CRC_OFFSET(message);
        byte crc = 0;
        
        if (size < ( position)) {
        	ESP_LOGE("Control", "frame format error (size = %d vs length = %d)", size, message[2]);
        	return 0;
        }

        for (int i = 2; i < position; i++)
            crc += message[i];

        return crc;
    }


    unsigned crc16(unsigned crc, unsigned char *buf, size_t len)
    { 
        while (len--) {
            crc ^= *buf++;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
        }
        return crc;
    }


};


#endif //HAIER_ESP_HAIER_H
1 Like

So this one should work for me too?

I can skip this below?

I’ve modified Haierv2.h in order to eliminate deprecated warnings and I had your same problem.
For me solution has been to delete

traits.set_supports_action(true);

Never mind it’s working thanks.

I have connected Wemos using you code. I can set modes, temperature etc, but hassio always shows climate entity as unavailableBez tytułu

I have tried to upgrade esphome on hassio, now i cant add wemos as climate device:

Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/components/esphome/init.py”, line 214, in on_login
entity_infos, services = await cli.list_entities_services()
File “/usr/local/lib/python3.8/site-packages/aioesphomeapi/client.py”, line 137, in list_entities_services
entities.append(cls(**kwargs))
File “”, line 9, in init
self.supported_modes = __attr_converter_supported_modes(supported_modes)
File “/usr/local/lib/python3.8/site-packages/aioesphomeapi/model.py”, line 245, in _convert_climate_modes
return [ClimateMode(val) for val in value]
File “/usr/local/lib/python3.8/site-packages/aioesphomeapi/model.py”, line 245, in
return [ClimateMode(val) for val in value]
File “/usr/local/lib/python3.8/enum.py”, line 339, in call
return cls.new(cls, value)
File “/usr/local/lib/python3.8/enum.py”, line 662, in new
raise ve_exc
ValueError: 6 is not a valid ClimateMode