Experience integrating Duux products?

@Humvee It connects to MQTT server port 443 with TLS enabled.

Just add to your mosquitto a new listener for this with no password and self signed certificates :


listener 443 0.0.0.0
cafile /etc/mosquitto/ssl/ca.crt
certfile /etc/mosquitto/ssl/cloudgarden.crt
keyfile /etc/mosquitto/ssl/cloudgarden.key
allow_anonymous true
max_connections -1
persistence true
2 Likes

Believe it or not, I’ve never made (self-signed?) certificates for a domain I do not own. LetsEncrypt makes it look easy. I’m not sure how to properly make the subdomains work. Here is what the internet told me so far:

openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 9999 -out ca.crt -subj "/CN=LocalRootCA"

openssl genrsa -out cloudgarden.key 2048
openssl req -new -key cloudgarden.key -out cloudgarden.csr -subj "/CN=cloudgarden"
openssl x509 -req -in cloudgarden.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out cloudgarden.crt -days 9999 -sha256

Full disclosure: This is for testing purposes only. I put all my barely updated security risk devices on a separate VLAN that cannot communicate with my main network, except for a single container, so I still need to make that proxy/relay work. I can’t have an open allow_anonymous listener on my main network because I’m paranoid afraid careful.

You don’t really need to fake the cloud garden domain (and id advise against it anyway), the devices don’t do any verification either of the common name, the SANs or the chain of trust.

In other words, as long as the server has a non-expired certificate, it’ll most likely work.

1 Like

I am trying to setup a second Mosquitto broker (in a docker container) to get the MitM / DNS re-write approach to connect my Duux Edge Oil. I have setup a DNS re-write in my AdGuard Home DNS Server (this works). I see my device is also reaching out to collector3.cloudgarden.nl

My Mosquitto has the following configuration, but I can’t seem to get data from the Duux Edge Oil. I can access the MQTT via MQTT explorer on both 1883 and 8883. If I add 443 as a listener, I see a message I need to use netcap to bind a privileged port (443) to mosquito and can’t do anything.

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
per_listener_settings true

listener 1883
allow_anonymous false
password_file /mosquitto/config/password.txt

# listener 443 0.0.0.0
# allow_anonymous true
# cafile /mosquitto/cert/ca.crt
# certfile /mosquitto/cert/mosquitto.crt
# keyfile /mosquitto/cert/mosquitto.key

listener 8883
allow_anonymous true
cafile /mosquitto/cert/ca.crt
certfile /mosquitto/cert/mosquitto.crt
keyfile /mosquitto/cert/mosquitto.key

Hope somebody can point me in the right direction. @KipK @Humvee @kimagure, sorry for tagging you but you seem to have some more knowledge on the MitM / DNS rewrite approach :slight_smile:

@timothi , It espect to connect to MQTT server on port 443 with TLS enabled.
You either need to run mosquitto as root to bind to 443, or use netcap yes.

Or run mosquitto over a docker container, and then map the 443 external port to internal port of your choice.

1 Like

Hi,
I recently purchased a relatively recent humidifier from Duux. The Neo. It is 2nd gen. I would like, as most people here, to integrate it locally in HA. It has the same MCU as shown in this thread multiple times.
See mine below:



I would like to flash the esp with esp home, with which I am relatively familiar.

However before I venture this way I would like know what to do once I have:

  1. Soldered tty-USB to RX, TX, 3V, GND
  2. Dump the original firmware
  3. Try to reflash the firmware to ensure I have a possibility to go back
  4. Flash esphome
  5. Then… I would need guidance to know How to I reverse engineer the Esphome siwtch and other mode commands.

It has on/off, normal mode (Constant running at a given level) , auto mode (set point humidity at a given level), and three “speed” 1, 2, 3 for humidity exhaust.
Can anyone give a few pointers on how to go about that?


The Dnat redirect of mqtt to my broker seemed like the most elegant and less intrusive method, and I would be very comfortable setting up an mqtt generic humidifier in Ha but I am at a loss to set up a network device or whatever to redirect the humidifier packets. Not sure what tool to use or where to start… And the information available in this thread is not quite noob proof on network packet redirect setup.

To reflash the existing board, you’d need to know, or be able to capture the commands it sends to the secondary board then be able to replicate them with the ESPHome config; it’s doable but quite advanced.

The DNAT option requires no modification of the board or firmware, just needs to “advanced” network configuration. Essentially, you’d need to have a router with enough configuration to DNAT traffic from the DuuX to your MQTT server, it’s also fairly advanced.

The last, and easiest option which I think was also already convered here, is to use a DNS override; also requires a router (or more specifically a DNS) which allows overrides; you override the address(es) that the DuuX talks MQTT to by setting the IP it resolves to, to the IP of your MQTT server, and then make sure your MQTT server has a listener with TLS on port 443 to match.

I managed to get my Duux ThreeSixty 2 linked to HomeAssistant using ESPHome today. Thanks to @jkufner for the inspiration of using the M5NanoC6.

For the ThreeSixty, I cut off one end of the Grove cable and had to replace that with a JST-XH 2.54, also with power and ground inverted like how it was needed for the Whisper.

You can then use the following ESPHome configuration snippet.
Make sure you configure the other needed parts to your own requirements.

esphome:
  name: duux-threesixty
  friendly_name: duux_threesixty

esp32:
  board: esp32-c6-devkitc-1
  variant: esp32c6
  flash_size: 4MB
  framework:
    type: esp-idf
    version: 5.1.2
    platform_version: 6.5.0 # Need at least 6.4 for ESP32-C6
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y

[... logger / wifi / api / ota ]

uart:
  tx_pin: GPIO2
  rx_pin: GPIO1
  baud_rate: 9600

tuya:

climate:
  - platform: tuya
    name: "heater"
    switch_datapoint: 1
    target_temperature_datapoint: 2
    current_temperature_datapoint: 3
    supports_heat: true
    supports_cool: false
    active_state:
      datapoint: 11
      heating_value: 0
    fan_mode:
      datapoint: 5
      high_value: 0
      medium_value: 1
      low_value: 2

When connected to HomeAssistant, you get a nice climate entity to control the device:
image

CC @KipK @japie101: Saw you guys were interested in ThreeSixty conversion.

1 Like

Wow, thanks for the mention, great progress!
Specific for the connections part: did you took any photos of the actions you described?

I did not, but it’s very easy to unscrew so I made you some.
The four screws are under the white sticker on the bottom, you can feel where the holes are by pressing around a bit on the sticker.I used a small knife to cut out the holes. For me one of the screws was right under the serial number so if you are gonna mess around, might want to take a picture of that before it’s ripped.

You’re going to find the original Duux control board (that’s been posted around here a couple of times already) also in this product.

Pinch/press the tab on the original JST to disconnect the stock ESP board

For the M5NanoC6, this is the cable you need to prepare. (!! the black and red wires are crossed)
It’s an official M5Stack Grove Cable. Cut off one side, and crimp it to JST-XH 2.54.

If you have never done this; you can find various assortment kits and criming tools on Amazon / AliExpress etc. for a couple $€.
There’s also plenty of Youtube videos explaining the process.

Then just insert the Grove side into the M5NanoC6, and the JST into the mainboard of the ThreeSixty.
I used a bit of tape to make sure the wires can not be pulled into the fan.

If you ever want to revert the device to be fully original, just screw it open again, take out your M5Nano and put back the original JST and screw it back together. :grinning:

And yes, mine needs a bit of cleaning :see_no_evil:

3 Likes

Thank you so much for the detailed instructions along with the photos, appreciate the time and effort you invested in this project itself and describing/documenting it for the community. :ok_hand:

Hi,
This is to give an update, and hopefully help others get through it a bit more easily than I did. But I decided as recommended to go the MQTT redirect way.
I did the following:

  1. set up a separate MQTT broker on my NAS using a container, TLS enabled with adequate certificates, listening on 443 as detailed by @Humvee and @hamido here and a lot more searching and failing online. Was the absolute hardest part for me.
  2. setup an adguard home (HA Add-on), with DNS rewrite from the Duux device to the MQTT broker, as suggested by @timothi. That somehow worked flawlessly
  3. Bridged the new TLS MQTT broker to my main HA MQTT broker. Best guide I found is here: Mosquitto MQTT Bridge-Usage and Configuration . But there are many other good ones. That was pretty easy having been through the step 1.
    Once it is all publishing in HA MQTT, it is all clear.
    Here is a typical JSON mqtt payload for the Duux Neo humidifier on topic “sensor/[MACADDRESS]/in”:
{
  "sub": {
    "Tune": [
      {
        "uid": "<serial>",
        "id": 1,
        "power": 1,
        "speed": 2,
        "timer": 0,
        "mode": 1,
        "sp": 60,
        "hum": 56,
        "err": 0,
        "timRm": 0,
        "night": 0
      }
    ]
  }
}
  1. Set up a number of MQTTdevice and attached entities in configuration.yaml to integrate in HA frontend, based on example shown here by @csmale
    Here is my input to yaml for those interested:
mqtt:
  select:
    - name: "Duux Neo Speed"
      command_topic: "sensor/<MACADDRESS>/command"
      command_template: "tune set speed {% if value == 'low' %}0{% elif value == 'medium' %}1{% elif value == 'medium' %}2{% else %}2{% endif %}"
      state_topic: "sensor/<MACADDRESS>/in"
      value_template: "{% if value_json.sub.Tune[0].speed == 0 %}low{% elif value_json.sub.Tune[0].speed == 1 %}medium{% else %}high{% endif %}"
      unique_id: duux_speed_mqtt
      device:
        manufacturer: Duux
        model: "Neo"
        identifiers: "<MACADDRESS>"
      options:
        - "low"
        - "medium"
        - "high"
  humidifier:
    - name: "Duux Neo humidifier"
      unique_id: duux_humidifier_mqtt
      device:
        manufacturer: Duux
        model: "Neo"
        serial_number: <Serial Number>
        identifiers: "<MACADDRESS>"
      device_class: "humidifier"
      availability:
        - topic: "sensor/<MACADDRESS>/online"
          value_template: "{{ value_json.online }}"
          payload_available: true
          payload_not_available: false
      command_topic: "sensor/<MACADDRESS>/command"
      command_template: "tune set power {{ value }}"
      state_topic: "sensor/<MACADDRESS>/in"
      state_value_template: "{{ value_json.sub.Tune[0].power }}"
      payload_on: 1
      payload_off: 0
      current_humidity_topic: "sensor/<MACADDRESS>/in"
      current_humidity_template: "{{ value_json.sub.Tune[0].hum }}"
      target_humidity_command_topic: "sensor/<MACADDRESS>/command"
      target_humidity_command_template: "tune set sp {{ value }}"
      target_humidity_state_topic: "sensor/<MACADDRESS>/in"
      target_humidity_state_template: "{{ value_json.sub.Tune[0].sp }}"
      mode_state_topic: "sensor/<MACADDRESS>/in"
      mode_state_template: "{% if value_json.sub.Tune[0].mode == 0 %}normal{% elif value_json.sub.Tune[0].mode == 1 %}auto{% endif %}"
      mode_command_topic: "sensor/<MACADDRESS>/command"
      mode_command_template: "tune set mode {% if value == 'normal' %}0{% elif value == 'auto' %}1{% endif %}"
      modes:
        - "normal"
        - "auto"
      qos: 0
      min_humidity: 30
      max_humidity: 80
   sensor:
    - name: "Duux Neo humidity"
      device:
        manufacturer: Duux
        model: "Neo"
        identifiers: "<MACADDRESS>"
      state_topic: "sensor/<MACADDRESS>/in"
      unit_of_measurement: "%"
      unique_id: "duux_neo_current_humidity"
      value_template: >-
        {{ value_json.sub.Tune[0].hum }}
      device_class: humidity
  binary_sensor:
    - name: "Duux Neo problem"
      device:
        manufacturer: Duux
        model: "Neo"
        identifiers: "B0:A7:32:30:18:8C"
      state_topic: "sensor/b0:a7:32:30:18:8c/in"
      unique_id: "duux_neo_error"
      value_template: "{% if value_json.sub.Tune[0].err == 0 %}0{% else %}1{% endif %}"
      payload_off: "0"
      payload_on: "1"
      device_class: problem

That leads to a fully integrated Duux device:


image
image

I hope this helps someone out there.

@dyllaann , I did the esphome way for my threesixty 2 with esp32, thanks for your configuration file, everything works perfect.

1 Like

Great Post… Setup the DNAT using 443 → 8883 redirect on my Ubiquiti Gateway Ultra and manual tests (TELNET) looked hopefull, however nothing showed up in MQTT.

So I looked into my Pi-Hole to see if I could find this host collector3.cloudgarden.nl
However I couldn’t see any entry, so started to dig a little deeper.

Started a TCPDUMP on the router and monitored the DUUX traffic while operating the DUUX APP on my mobile phone and noticed frequent contact with:
ec2-35-156-44-172.eu-central-1.compute.amazonaws.com.8886.

Did I miss some information in this thread completely?

The AWS address is likely the loadbalancer address they’re using in AWS for their service, they’ll have their own network addresses pointing at them.

The fact there there is a number in “collector3” suggests there may be more than one. If I remember right, there was also something like api.cloudgarden.nl

What you’ll want to do is check your DNS for any lookups to sub domains of cloudgarden.nl and

I don’t remember if they had a hardcoded DNS in the DuuX, so you may either want to capture ALL DNS lookups, even those sent to remote servers like 8.8.8.8 etc as well as local ones, or, capture all requests to DNS ports. A good start is to assume they’re using plain old DNS and capture everything for port 53, the DNS port.

1 Like

Ok, let me check and capture using:

tcpdump -i br55 udp port 53 and ( host 192.168.x.y or host 192.168.x.z )

where 55 is the specific VLAN the IOT is running.
First thing that popped up a3.tuyaeu.com

I do indeed think the AWS is a loadbalancer as I see multiple similar entries flying by.

Stuck in the same situation. Have rewritten collector3.cloudgarden.nl and collector.cloudgarden.nl to my MQTT broker. These are the only addresses I can see from AdGuard Home. I simply added *.cloudgarden.nl now. With nslookup everything is properly forwarded.

In my docker I have changed 443 to 8883. I am able to directly connect on 8883. MQTT explorer successfully connects to 8883 with TLS enabled.

Seems like the only thing I can’t check / validate is if the certificates are working, I have used these steps here. This is where my knowledge runs out - anyone can share their way of creating the self-signed certificates?