Dishwasher - Candy simply FI - CDI 6015 WiFi

In addition to @jezcooke suggestion, you may try with curl -v instead of postman. The output may be more useful.

I saw your postman collection and tried it but with no luck. I removed the extra api/v1 in the path and made the request.

It was working fine but suddenly my hob does not connect to my wifi anymore.

I tried to enroll it again using your request but if I set encrypted=0 I get 401. If I set encrypted=1 it does not work. I get 200 but it does not connect to the wifi

Hi Everyone,

Just got myself a Hoover tumble dryer which uses the hOn app.

Hoover and Candy are both owned by Haier so I was hoping they are the same however I followed some of the above examples (tried postman, Curl) and I keep getting “connection refused”

Tried also MelvinGR’s tool but I get

error: connect
error: get_candySimplify_data, could not get data from server

can anyone point me best way to start trying to identify if there is any local api available ?

2 Likes

Hi all,
Candy ROW 4964DXHS\1-S washer-drier here, connected to an SSID network with no internet access.
This request:

curl http://192.168.xx.yy/http-read.json?encrypted=0

get sometimes data, sometimes request fails with:

curl: (7) Failed to connect to 192.168.xx.yy port 80: Connection refused

Maybe is it some request rate limit?
Complete results:

{
	"statusLavatrice":{
		"WiFiStatus":"0",
		"Err":"0",
		"MachMd":"7",
		"Pr":"10",
		"PrPh":"0",
		"PrCode":"0",
		"SLevel":"3",
		"Temp":"40",
		"SpinSp":"10",
		"Opt1":"0",
		"Opt2":"0",
		"Opt3":"0",
		"Opt4":"0",
		"Opt5":"0",
		"Opt6":"0",
		"Opt7":"0",
		"Opt8":"0",
		"Opt9":"0",
		"Steam":"0",
		"DryT":"0",
		"DelVal":"18",
		"RemTime":"60",
		"RecipeId":"0",
		"Lang":"0",
		"FillR":"26",
		"DisTestOn":"0",
		"DisTestRes":"0",
		"CheckUpState":"0",
		"T0W":"61",
		"TIW":"0",
		"T0R":"50",
		"numF":"0",
		"unbF":"158",
		"unbC":"250",
		"NtcW":"148",
		"NtcD":"274",
		"motS":"0",
		"APSoff":"0",
		"APSfreq":"62460",
		"chartL":"153562"
	}

It would be great to understand parameters… Maybe one of these is the countdown of the running program, useful for trigger a notification?
Program countdown on the washer-drier display is unreliable.
-f

I have a similar appliances. I made an appdaemon script based on jezcooke code: https://gist.github.com/dzamlo/8b301e950927ef73d41eaffda086b123

With that can then get a notification for the end of the program when the state transition to finished using an automation. You also get the estimated time of the end of the program.

Unfortunately, this is very unreliable. At some point, the machine stop responding and the only way to talk to it again is to unplug the machine.

But I found that the machine send its state to simplyfimgmt.candy-hoover.com at a regular interval and do that reliably, as long as you don’t send request to the machine. My plan is to setup my dns so that this name resolve to my home assistant IP and somehow make it listen for these update. Unfortunately, it seems I cannot do that with appdaemon (the port 80 of the host is not exposed to the container). I’m trying to understand how to implement a custom integration to do that.

Here are the meaning of some of the values:
MachMd: machine mode, the state of the machine: 1: not yet started, 2: program running, 3: paused, 5: delayed start, 7: finished
DelVal: the delayed start time, in minutes
RemTime: the remaing time, in second (on my machine it’s always a multiple of 60)
FillR: fill ratio (I guess is a value between 0 and 100, stay at 0 for some program)
CheckUpState: the state of the check up test (I think you can run that from the application, didn’t check). 0 means the test wasn’t run, 1 the test is running and 2 the test ended or the test failed (I don’t know).
The various Pr values indicate which program is selected/running. You can get a json describing the program (and mapping the program number to names) with the cloud api.

You can use RemTime

I checked DelVal and RemTime values during some washing programs, dumping them with a curl loop script: these are quite unreliable as per my tests.
I’m working on MachMd, triggering the switch state from 2 to 7 as action for the notification.

If I get a reply without encryption (=0), what command should I use? I tried “rest” but got no results.

Some updates (cloud connection): now header Salesforce-Auth=1 is required

And there are some url errors in the postman template ({{CANDY_BASE_URI}}api/v1/something instead of {{CANDY_BASE_URI}}something because CANDY_BASE_URI includes api/v1/)

I cannot test right now, but it should be "{{ value_json['statusLavatrice']['DryT'] }}"

1 Like

I’ve got this slide from candy support. Some models will be switched to hOn during this year

1 Like

Hello, i share my python code . It works with my washing machine. It return remaining time :

# Candy SimplyFI 
import json
import urllib.request

candy_reponse = urllib.request.urlopen("http://192.168.1.22/http-read.json\?encrypted\=1").read().decode()
#candy_reponse = '7B0D0A09227374617475734C6176617472696365223A7B0D0A09092257694669537461747573223A2231222C0D0A090922457272223A22323535222C0D0A0909224D6163684D64223A2231222C0D0A0909225072223A223135222C0D0A09092250725068223A2230222C0D0A090922534C6576656C223A2232222C0D0A09092254656D70223A223930222C0D0A0909225370696E5370223A2234222C0D0A0909224F707431223A2230222C0D0A0909224F707432223A2230222C0D0A0909224F707433223A2230222C0D0A0909224F707434223A2230222C0D0A0909224F707435223A2230222C0D0A0909224F707436223A2230222C0D0A0909224F707437223A2230222C0D0A0909224F707438223A2230222C0D0A090922537465616D223A2230222C0D0A09092244727954223A2230222C0D0A09092244656C56616C223A22323535222C0D0A09092252656D54696D65223A22313336222C0D0A0909225265636970654964223A2230222C0D0A090922436865636B55705374617465223A2230220D0A097D0D0A7D'
#print(candy_reponse)

candy_json = bytes.fromhex(candy_reponse).decode()
#print(candy_json)
candy_str = str(candy_json)
candy_data = json.loads(candy_str)
statusLavatrice = candy_data["statusLavatrice"]
#print(statusLavatrice)
RemTime = statusLavatrice["RemTime"]
print(RemTime)
2 Likes

If some one is interesetd i have reveresed the bluetooth enrolement procedure.
I have tested it with a candy rapido washing machine and i was able to connect it to my Wi-Fi.
This is the java code that i had take from the android application.

public class SimplpyFiBle {
    
    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    private static final int POLYNOMIAL = 25443;
    private static final int SECURITY_WEP = 2;
    private static final int SECURITY_WPA = 3;
    
    public static final byte[] intToByteArray(int i) {
        return new byte[]{(byte) (i >>> 8), (byte) i};
    }
    
    public static String bytesToHex(byte[] bytes) {
        String result = "";
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            result += HEX_ARRAY[v >>> 4];
            result += HEX_ARRAY[v & 0x0F];
        }
        return result;
    }
    
    public static int crc16(byte[] bArr) {
        int i = 65535;
        for (byte b : bArr) {
            i ^= b & 255;
            for (int i2 = 0; i2 < 8; i2++) {
                i = (i & 1) != 0 ? (i >>> 1) ^ POLYNOMIAL : i >>> 1;
            }
        }
        return (~i) & 65535;
    }
    
    public static void appendCRC(byte[] bArr, int i) {
        int length = (bArr.length - i) - 2;
        byte[] bArr2 = new byte[length];
        System.arraycopy(bArr, i, bArr2, 0, length);
        byte[] intToByteArray = intToByteArray(crc16(bArr2));
        int length2 = bArr.length - 2;
        bArr[length2] = intToByteArray[0];
        bArr[length2 + 1] = intToByteArray[1];
    }
    
     public static void main(String []args){
        String ssid = "WIFI_SSID";
        String pass = "WIFI_PASSWORD";
        int security = SECURITY_WPA;
        String key = "HOMEASSISTANTKEY";
        
        if (key.length() != 16) {
            System.out.println("Key length must be 16, current is: " + key.length());
            return;
        }
        
        byte[] bytes = String.format("NTW_MODE=%s&SSID=%s&PASSWORD=%s&ENCRYPT_KEY=%s", Integer.valueOf(security), ssid, pass, key).getBytes();
        int length = bytes.length + 3 + 2;
        byte[] bArr = new byte[length];
        bArr[0] = -91;
        bArr[1] = (byte) length;
        bArr[2] = 22;
        System.arraycopy(bytes, 0, bArr, 3, bytes.length);
        appendCRC(bArr, 0);
        System.out.println(bytesToHex(bArr));
     }
}

This will produce an string that can be used with this app https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en&gl=US
to configure the device.

The steps to configure the device are:

  • Put the device into ble mode
  • Generate the hex string, you can go here: https://www.tutorialspoint.com/compile_java_online.php to run the java code online.
  • Open the nrf connect app
  • Perform a ble scan
  • Connect to the device, you should see a service with UUID 0xA002 click on it
  • On the characteristic with UUID 0xC302 perform a write and select as value type “BYTEARRAY” and past the generated string from the priouse step as value
  • Now your device should be connecting to your Wi-Fi.

I’d like to share my current config:
I use a local polling of the washer with an automation: while the washer is responsive on the network (ping state on), system is waiting for MachMd attribute switch from 2 to 7.
scan_interval values could be more aggressive, I agree, anyway for me it’s ok to get the notification with some minutes delay.
I’m not satisfied about the command line sensor, now checked fulltime despite the washer is off - nice the thread about disabling a sensor - any hint about it?

configuration.yaml file:

binary_sensor:
  - platform: ping
    host: 192.168.202.156
    name: "Candy"
    count: 2
    scan_interval: 30

sensor:
  - platform: command_line
    name: Candy MachMd
    command: "curl -s http://192.168.202.156/http-read.json?encrypted=0 | jq .statusLavatrice.MachMd"
    scan_interval: 30
    command_timeout: 5
    value_template: '{{ value | replace("\"", "") | int }}'

automations.yaml file:

- id: '1612013181362'
  alias: Candy program notifier
  description: ''
  trigger:
  - platform: state
    entity_id: sensor.candy_machmd
    from: '2'
    to: '7'
  condition:
  - condition: state
    entity_id: binary_sensor.candy
    state: 'on'
  action:
  - service: persistent_notification.create
    data:
      message: '{{ now().strftime("%H:%M %Y-%m-%d") }}: Program finished!'
      title: Candy
  mode: single
1 Like

Is there any option to connect all commands in one? At this point there are too many asks from my IP and and the sensors are offline at 70% of time.

You can use attribute_templates to make the attribute of the same sensor. And then you can transform the attributes into sensors with template sensors.

With my washing machine, it start to fail after some time even with only one request per minutes

Watching with intent as I’m just starting to plan a new kitchen and Candy WiFi ovens are half the price of others so of interest!

1 Like

Thank you so much for sharing your configuration.I had to do some syntax adjustments to the MachMd sensor but it work. Namely:

  1. adding escaped quotes to the URL queried by curl

command: "curl -s \"http://192.168.202.156/http-read.json?encrypted=0\" | jq .statusLavatrice.MachMd"

  1. removing the cast to int at the end of value_template
    value_template: '{{ value | replace("\"", "") }}'

Unfortunately for me it’s too hit and miss. It could be that the timing needs adjusting but I suspect that the culprit is the curl script that does not always answer. I monitor the state from the terminal when I’m debugging and I don’t always get an answer.

I still don’t get why the automation is not triggered, maybe when the curl request fails the sensor state becomes undefined or blank therefore there is no 2 -> 7 transition but 2 -> ? -> 7

Maybe the template needs to default to “2” in case of timeout?

I’m debugging this with 2 -> 3 (Paused) and the automation is triggered roughly 50% of the times.

I would like to fine-tune your configuration because it basically works.

Otherwise I’d have to resort to something more reliable but a lot more complicated, i.e. sniffing the packet that the machine sends to the Candy Cloud when it’s finished.

I already did that, if someone’s interested:

<machine_ip> -> 52.213.231.49 HTTP

[truncated]GET /api/av1/listen.json?macAddress=XXXXXXXXXXX&encrypted=0&macAddress=XXXXXXXXXXXX&WiFiStatus=0&Err=0&MachMd=7&Pr=1&PrPh=0&PrCode=0&SLevel=0&Temp=40&SpinSp=10&Opt1=0&Opt2=0&Opt3=0&Opt4=0&Opt5=0&Opt6=0&Opt7=0&Opt8=0&Opt9=0&St
Host: simplyfimgmt.candy-hoover.com

This however is way more complicated and probably not even doable if you don’t have the proper hardware. Your router needs to be able to run some kind of script and trigger some other mechanism that Home Assistant can read reliably.

***** EDIT ***** 16/03/2021
I think I got this to work by lowering the polling frequency of the sensor and increasing the timeout. I also got rid of the binary sensor and I integrated the ping in the command sensor. Here’s my code:

sensor:
  - platform: command_line
    name: Candy MachMd
    command: "ip=192.168.1.10; ping $ip -c 1 -W 2 > /dev/null; if [ $? -eq 0 ]; then res=$(curl -s \"http://$ip/http-read.json?encrypted=0\"); if [ $? -eq 0 ]; then echo $res | jq .statusLavatrice.MachMd; else echo \"\"999\"\"; fi; else echo \"\"0\"\"; fi"
    scan_interval: 60
    command_timeout: 29
    value_template: '{{ value | replace("\"", "") }}'

The readability is not great but basically it works like this:
no ping response -> 0
ping response but empty or no curl response -> 999
ping response, curl response -> value of MachMd

This way I figured out that during wash I was getting 2 / 999 / 2 / 999 / 2 / 999 / 7 / 999 / 7 / 999

I was polling every 30 seconds with 10 seconds timeout. Now I interrogate the machine every 60 seconds and I wait 29 seconds before timeout. I only tried it once so far but I got no 999 and the automation worked.