Preface

So, I live in a fairly damp London home. We try and run a dehumidifier reguarlly but we have diffrent rates of electric. So natrually, I wanted to mess around with it and see if I could hook it up to home assistant…

I was orignally planning just to connect up to a button on the controller board but it used some dumb touch sensors which are just a pain to deal with. I also wanted the humidity reading for my home assistant dashboard. So I decided to find another way!

There are two controller boards, one is the buttons and display out and the other is what actuarly turns it on and off along with giving the read out from the humidity sensor.

Getting the information out from UART

I have a SH-U9C5 serial adapter which I used connected up to the cable. I had to re-pin the cable, which looked a little like this:

image of cable that I modded, it has two connectors either side but on one side it also has some jumper pins coming off I had to get the cables connected the right way round but its only two ways so all was good after the second try. I used picocom to connect: sudo picocom /dev/ttyUSB0 --imap spchex,tabhex,crhex,lfhex,nrmhex,8bithex --baud 9600. This output the data as HEX in a nice readable way. This is what I would see when toggling the power button:

[bb][51][80][31][00][01][00][00][00][b2] # POWER ON
[bb][51][00][31][00][01][00][00][00][32] # POWER OFF

To test that this worked, I flipped the RX and TX so I could transmit the power off and on commands. Like magic, it worked! It was still connected up to my laptop but I could controll the device! This was the first major step to getting this to work!

So, I grabbed a ESP-32 I had lying around. Flashed it with ESP-HOME and added this as the config:


uart:
  tx_pin: GPIO00
  rx_pin: GPIO01
  baud_rate: 9600
  parity: NONE

switch:
  - platform: template
    name: "Dehumidifier"
    turn_on_action:
      - uart.write: [0xBB, 0x51, 0x80, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB2]
    turn_off_action: 
      - uart.write: [0xBB, 0x51, 0x00, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32]

As soon as I tried… it… did nothing… I had the fucking RX and TX the wrong way. Now when I flip it, it works! HELL YEAH!

I mean I can only turn it off and on at the moment. Not really the most impressive thing. Next I wanted to get the humidity reading off the device.

Reading the humidity sensor

So, how this device works is it sends back data to the controller board with the humidity on. It sends it in HEX format. I wasn’t really sure what I was looking at. I tried putting the value (with out the start and end bites) into a hex to int converter but it still made no sence. I had a seperate humidity sensor so I was sure the value I was getting was wrong, it was also to like 20 decimal places.

For context, this is what I was seeing:

[bb][51][00][31][36][01][00][40][00][00][15][00][40][00][fd]
[bb][51][00][31][36][01][00][40][00][00][14][00][40][00][fc]
[bb][51][00][31][36][01][00][40][00][00][15][00][40][00][fd]
[bb][51][00][31][36][01][00][40][00][00][14][00][40][00][fc]

As it turns out, I don’t need the whole string, I just need the 4th bit. In the above example it is just showing 36. Converted that and other values I was seeing into a int and it was about the same as my humidity sensor! This was a huge result! All I had to do now was add this as a sensor to ESP-HOME. How hard could that be?

Humitity sensor in ESP-HOME

So, getting the data into ESP-HOME was something I had no clue how to do. I found THIS, spesifically Simon Bok’s comment. I basicly just copied the humidity sensor part leaving me with this:


uart:
  tx_pin: GPIO00
  rx_pin: GPIO01
  baud_rate: 9600
  parity: NONE
  debug:
    direction: BOTH
    dummy_receiver: true
    sequence:
    - lambda: |-             
        UARTDebug::log_hex(direction, bytes, ',');   

        if (direction == UART_DIRECTION_RX) {
          if (bytes[0] == 0xBB) {
            float humidity = bytes[4];
            id(humidity_sensor).publish_state(humidity);
          }
        }

switch:
  - platform: template
    id: dehumidifier_switch
    name: "Dehumidifier Switch"
    turn_on_action:
      - uart.write: [0xBB, 0x51, 0x80, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB2]
    turn_off_action: 
      - uart.write: [0xBB, 0x51, 0x00, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32]

sensor:
  - platform: template
    name: "Humidity reading"
    id: humidity_sensor
    unit_of_measurement: "%"
    device_class: "humidity"
    state_class: "measurement"
  - platform: template

Now I have a entity called humidity_sensor. This just work. I mean don’t get me wrong, it took me a while to figure out what I needed to copy and tried lots of other things first. The main thing was creating a if statment to only look at RX and ensuring the first bytes were the 0xBB ones. I found this has been most reliable.

As it turns out it also had a tempriture sensor which I added with this:

uart:
  ...
  debug:
    ...
    - lambda: |-             
        ...
        if (direction == UART_DIRECTION_RX) {
          if (bytes[0] == 0xBB){
            float temperature = bytes[10];
            id(temperature_sensor).publish_state(temperature);
          }
        }
... 

sensor:
  ...
  - platform: template
    name: "Temperature reading"
    id: temperature_sensor
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1

Once again, taken from that comment.

The last little bits.

One thing that was really annoying was the HA switches would automaticly go back to off after a few seconds of turning the device on. Now it didn’t turn it off, just the switch state changed. I realised what I needed to do was pull in the power state. In the UART string I was reciving, it would also be telling me the power state.

BB,51,80,31,40,01,00,03,00,00,14,00,40,00,49 # POWERED ON
BB,51,00,31,40,01,00,03,00,00,14,00,40,00,49 # POWERED OFF

The second byte changed from 80 to 00. This is what stated weather the device was on or off. All I need to do was add another if statment to the lambda, like this:

if (bytes[0] == 0xBB && bytes[2] == 0x80) {
    id(dehumidifier_switch).publish_state(true);
}
if (bytes[0] == 0xBB && bytes[2] == 0x00) {
    id(dehumidifier_switch).publish_state(false);
}

This updates the switch state depending on the state reported back to the esp. Nice!

Now, I did figure out some other bits but eh, I don’t really want to talk about them. I will leave what I know so far about the hex here:

BB,51,80,31,3F,01,00,83,00,00,14,00,40,00,C8
S  ?  pw SP H  ?  ?  ?  ?  TP T  ?  ?  ?  F
0  1  2  3  4  5  6  7  8  9  10 11 12 13 14

S - start bit?
pw - power status
H - humidity
SP - Speed? 0x12 0x32 and default maybe 0x31
T - Tempriture
F - Tank fill level? Really might not be, the value seems to not change much when the tank isn't in and changes a lot when I wiggle a magnet but further investigation is required.
TP - Tank presence (changes from 04 to 00 when not present (or the other way around))
If you want my full config, you can find it here
esphome:
  name: esp32-dehumidifier
  friendly_name: bedroom-dehumidifier
  on_boot:
    - priority: 800
      then:
        - uart.write: [0xBB, 0x51, 0x00, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32]

uart:
  tx_pin: GPIO00
  rx_pin: GPIO01
  baud_rate: 9600
  parity: NONE
  debug:
    direction: BOTH
    dummy_receiver: true
    sequence:
    - lambda: |-             
        UARTDebug::log_hex(direction, bytes, ',');   

        if (direction == UART_DIRECTION_RX) {
          if (bytes[0] == 0xBB) {
            float humidity = bytes[4];
            id(humidity_sensor).publish_state(humidity);
          }
          if (bytes[0] == 0xBB){
            float temperature = bytes[10];
            id(temperature_sensor).publish_state(temperature);
          }
          if (bytes[0] == 0xBB && bytes[2] == 0x80) {
            id(dehumidifier_switch).publish_state(true);
          }
          if (bytes[0] == 0xBB && bytes[2] == 0x00) {
            id(dehumidifier_switch).publish_state(false);
          }
          if (bytes[0] == 0xBB) {
            float tank = bytes[14];
            id(tank_sensor).publish_state(tank);
          }
          if (bytes[0] == 0xBB && bytes[9] == 0x00) {
            ESP_LOGD("tank_installed", "installed"); 
          }
          if (bytes[0] == 0xBB && bytes[9] == 0x04) {
            ESP_LOGD("tank_installed", "uninstalled"); 
          }
          //if (bytes[0] == 0xBB && bytes[3] == 0x32) {
          //  ESP_LOGD("speed_state", "default"); 
          //}
          //if (bytes[0] == 0xBB && bytes[3] == 0x12) {
          //  ESP_LOGD("speed_state", "12"); 
          //}
          //if (bytes[0] == 0xBB && bytes[3] == 0x31) {
          //  ESP_LOGD("speed_state", "31"); 
          //}
        }

switch:
  - platform: template
    id: dehumidifier_switch
    name: "Dehumidifier Switch"
    turn_on_action:
      - uart.write: [0xBB, 0x51, 0x80, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB2]
    turn_off_action: 
      - uart.write: [0xBB, 0x51, 0x00, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32]
  - platform: template
    name: "Dehumidifier Speed"
    id: dehumidifier_speed_switch
    turn_on_action:
      - uart.write: [0xBB, 0x51, 0x80, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2]
    turn_off_action:
      - uart.write: [0xBB, 0x51, 0x80, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92]

sensor:
  - platform: template
    name: "Humidity reading"
    id: humidity_sensor
    unit_of_measurement: "%"
    device_class: "humidity"
    state_class: "measurement"
  - platform: template
    name: "Temperature reading"
    id: temperature_sensor
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
  - platform: template
    name: "tank reading"
    id: tank_sensor
    unit_of_measurement: ""
    state_class: "measurement"

Conclusion

This was a really fun project, I learnt a lot about UART and got that itch for modding/hacking again. This wasn’t the most complicated thing to get working, so it was perfect for a first project.

A artical I want to mention is this: This blog was someone’s attempt at doing this. They used the button method on the touch sensors. It didn’t work for me but was a cool write up