Dishwasher - Candy simply FI - CDI 6015 WiFi

Here’s what I did:

  • Download the decrypt script: curl -SLO https://raw.githubusercontent.com/Alamot/code-snippets/master/crypto/xorknown.py
  • Make it executable: chmod +x ./xorknown.py
  • Get encrypted data and convert to ascii: curl -s http://[APPLIANCE-IP]/http-read.json?encrypted=1 | xxd -r -p > ./crypted.txt
  • Decrypt data: ./xorknown.py ./crypted.txt '{"status' 16

The script will return partially decrypted data, go on adding known text to improve it.
Problem is that the API returns its data localized. Because of this you can not always use the same string.
I had to use '{"statusHob":{"S as complete string since I own an induction hob and the first field of the object was not WiFiStatus but StatoWiFi.

The complete result is:

{
  "statusHob": {
    "StatoWiFi": "1",
    "Z1status": "0",
    "Z1pan": "0",
    "Z1hot": "0",
    "Z1combi": "0",
    "Z1low": "0",
    "Z1power": "0",
    "Z1error": "00",
    "Z1timeh": "0",
    "Z1timem": "0",
    "Z2status": "0",
    "Z2pan": "0",
    "Z2hot": "0",
    "Z2combi": "0",
    "Z2low": "0",
    "Z2power": "0",
    "Z2error": "00",
    "Z2timeh": "0",
    "Z2timem": "0",
    "Z3status": "0",
    "Z3pan": "0",
    "Z3hot": "0",
    "Z3combi": "0",
    "Z3low": "0",
    "Z3power": "0",
    "Z3error": "00",
    "Z3timeh": "0",
    "Z3timem": "0",
    "Z4status": "1",
    "Z4pan": "1",
    "Z4hot": "0",
    "Z4combi": "0",
    "Z4low": "0",
    "Z4power": "1",
    "Z4error": "00",
    "Z4timeh": "7",
    "Z4timem": "59",
    "lock": "0",
    "alarmh": "0",
    "alarmm": "0"
  }
}
3 Likes

Ok, here are the strings to use for each appliance:

  • '{"statusHob":{"StatoWiFi' for hob
  • '{"statusDWash":{"StatoWiFi' for dishwasher
  • '{"statusLavatrice":{"WiFiStatus' for washing mashine
  • '{"statusTD":{"StatoWiFi' for tumble dryer
  • '{"statusRefrigerator":{"WiFi' for fridge
  • '{"StatusHood":{"WiFiStatus' for hood
  • '{"statusForno":{"StatoWiFi' for hoven
1 Like

This is awesome. How would I get this info in to HA? Could I use some plugin to pull the info in?

No, there’s no plugin at the moment. I ended up using a command line sensor to fetch and decrypt the information and then a bunch of template sensors to split the values into some entities

Do you own a washing machine too? Since I can`t register my machine to the server, I cannot use the app. It would be nice to know, what we can do with the machine over wifi (start / stop …), but without the app I cannot sniff the communication…

No, I’m sorry

could you share the command line sensor and the template sensors? I have a dishwasher and i would like to know only when it is working

Once you’ve found the key the sensor is quite simple:

  - platform: command_line
    name: Induzione
    scan_interval: 30
    command: 'xortool-xor -h $(curl -sf http://PianoInduzione/http-read.json?encrypted=1") -s [KEY] | jq -cerM .statusHob'
    value_template: "{% if value_json.StatoWiFi | int == 1 -%}on{%- else -%}off{% endif %}"

In the command I read the encrypted JSON value from the hob and pass it to xortool-xor to have a plain JSON.
Then it’s just a matter of mapping the available fields to the sensor json_attributes and use them via template sensors.

2 Likes

I have a Hoover whashing dryer
I’ve got as far as here. I have the KEY and the phrase but I cannot get to work your example code on HA on Raspberry Pi4.
Should I first somehow install xortool or it is already present within HA?
What are the parts to be changed (IP, [KEY], .statusHob’) ?
Thanks in advance for your help

xortool-xor is not part of Home Assistant and needs to be installed on the system. Obviously .statusHob is specific to my case since I own an hob. Remove the jq part, check your json output and filter it accordingly using jq. It’s just a way to avoid extra parsing in the sensor

Unfortunatelly, it is impossible to install Python modules on Hass.io running on RP 4. I’ve been moving forward through a Python script via custom_script folder but as it is sandboxed it is not possible call necessary libraries or types.

key = "your key"
#import requests
#input_str = requests.get("http://YOUR.IP.GOES.HERE/http-read.json")
#input_str = data.get("sensor.lava_prep")
input_str = input_str.text
#bytes.fromhex(input_str)
#input_str = bytearray.fromhex(input_str).decode()
no_of_itr = len(input_str)
output_str = ""

for i in range(no_of_itr):
    current = input_str[i]
    current_key = key[i%len(key)]
    output_str += chr(ord(current) ^ ord(current_key))

logger.info(output_str)

At this point I’m at a dead end with my actual config.
The smallest thing I managed to do is to determine if it is on or off with this command line, hope it can be useful:

binary_sensor:
  - platform: command_line
    command: 'ping -W 1 -c 1 YOUR.IP.GOES.HERE > /dev/null 2>&1 && echo On || echo Off'
    name: 'Your Candy/Hoover appliace'
    scan_interval: 60
    payload_on: "On"
    payload_off: "Off"

A valid solutions should be creating a custom component, but I’m afraid it is too beyond my programming skills

Hi Massimiliano,
I have a Candy washing machine (BWM149PH7/1-S Bianca), the simply-fi app works correctly and I think I will soon buy a Candy dishwasher as well.
I wanted to try to enter it in Home Assistant, but I can’t get the key, I ran the following commands:

curl -s http://192.168.3.118/http-read.json?encrypted=1 | xxd -r -p > ./crypted.txt
./xorknown.py ./crypted.txt '{"statusLavatric' 16

return only:

Searching XOR-encrypted ./crypted.txt for string '{"statusLavatric' (max_key_length = 16)

Can you help me? (Anche in italiano :wink:)
Thanks

1 Like

Try reducing the string. The first curl worked fine?

I’m sorry but I think my washing machine behaves differently :sweat_smile:.
If I browser the url http://192.168.3.118/http-read.json?encrypted=1 a long hexadecimal text return me but if I launch the command curl -s http://192.168.3.118/http-read.json?encrypted=1 | xxd -r -p return me:

{
        "statusLavatrice":{
                "WiFiStatus":"1",
                "Err":"0",
                "MachMd":"1",
                "Pr":"3",
                "PrPh":"0",
                "PrCode":"3",
                "SLevel":"2",
                "Temp":"30",
                "SpinSp":"8",
                "Opt1":"0",
                "Opt2":"0",
                "Opt3":"0",
                "Opt4":"0",
                "Opt5":"0",
                "Opt6":"0",
                "Opt7":"0",
                "Opt8":"0",
                "Opt9":"0",
                "Steam":"0",
                "DryT":"0",
                "DelVal":"226",
                "RemTime":"4740",
                "RecipeId":"0",
                "Lang":"0",
                "FillR":"32",
                "DisTestOn":"0",
                "DisTestRes":"0",
                "CheckUpState":"0"
        }
}

I just don’t have to run the next command because there isn’t a key, or at least I think this

It looks like the washing machine is not using the xor encryption. Good to know.

Havign the plain json you can go on and implement the sensors in home assistant

1 Like

Thank you, managed to get data from my new Candy Rapido RO4 Washing machine using steps from Maxcanna post. Got these values:

Plaintext: {
        "statusLavatrice":{
                "WiFiStatus":"0",
                "Err":"0",
                "MachMd":"2",
                "Pr":"11",
                "PrPh":"2",
                "PrCode":"3",
                "SLevel":"2",
                "Temp":"60",
                "SpinSp":"8",
                "Opt1":"0",
                "Opt2":"0",
                "Opt3":"0",
                "Opt4":"0",
                "Opt5":"0",
                "Opt6":"0",
                "Opt7":"0",
                "Opt8":"0",
                "Opt9":"0",
                "Steam":"5",
                "DryT":"0",
                "DelVal":"0",
                "RemTime":"3180",
                "RecipeId":"0",
                "Lang":"17",
                "FillR":"92",
                "DisTestOn":"0",
                "DisTestRes":"0",
                "CheckUpState":"0",
                "T0W":"12",
                "TIW":"27",
                "T0R":"0",
                "numF":"1",
                "unbF":"0",
                "unbC":"0",
                "NtcW":"483",
                "NtcD":"9",
                "motS":"64969",
                "APSoff":"166",
                "APSfreq":"60046",
                "chartL":"153122"
        }
}

Are there any other ways to create a sensor in HA without installing xortool-xor?

Here are the values for my clothing dryer

/http-config.json?encrypted=1
{"response":"SUCCESS"}

/http-getStatistics.json?encrypted=1
{
        "statusCounters":{
                "s1":"0",
                "s2":"1",
                "s3":"1",
                "s4":"0",
                "s5":"0",
                "s6":"0",
                "s7":"0",
                "s8":"0",
                "s9":"0",
                "s10":"0",
                "s11":"0",
                "s12":"0",
                "s13":"0",
                "s14":"0",
                "s15":"0",
                "s16":"0",
                "s17":"0",
                "s18":"0",
                "s19":"0",
                "s20":"0",
                "s21":"0",
                "s1t":"0",
                "s2t":"45",
                "s3t":"30",
                "s4t":"0",
                "s5t":"0",
                "s6t":"0",
                "s7t":"0",
                "s8t":"0",
                "s9t":"0",
                "s10t":"0",
                "s11t":"0",
                "s12t":"0",
                "s13t":"0",
                "s14t":"0",
                "s15t":"0",
                "s16t":"0",
                "s17t":"0",
                "s18t":"0",
                "s19t":"0",
                "s20t":"0",
                "s21t":"0",
                "sCottonBone":"0",
                "sCottonStore":"0",
                "sCottonHang":"0",
                "sCottonIron":"0",
                "sSynthBone":"0",
                "sSynthStore":"0",
                "sSynthHang":"0",
                "sSynthIron":"0",
                "sCottonT60":"2",
                "sCottonT120":"0",
                "sCottonT180":"0",
                "sCottonTMax":"0",
                "sSynthT60":"0",
                "sSynthT120":"0",
                "sSynthT180":"0",
                "sSynthTMax":"0"
        }
}

/http-read.json?encrypted=1
{
        "statusTD":
{"StatoWiFi":"0",
                "StatoTD":"2",
                "CodiceErrore":"0",
                "Pr":"1",
                "PrPh":"2",
                "RemTime":"28",
                "DryLev":"2",
                "Time":"0",
                "Rapido":"0",
                "Opt1":"0",
                "Opt2":"0",
                "Opt3":"0",
                "Opt4":"0",
                "Opt5":"0",
                "Opt6":"0",
                "Opt7":"0",
                "Opt8":"0",
                "Refresh":"0",
                "CleanFilter":"0",
                "WaterTankFull":"0",
                "DryingManagerLevel":"0",
                "DelVal":"0",
                "DoorState":"1",
                "RecipeId":"NULL",
                "CheckUpState":"0"
        }

Hello. I just bought a candy hcf3c7lfw dishwasher. I’m trying to follow the instructions.

1.-download decrypt script and make it executable.OK
2.-get data and converted to ascii. I’ve got this:
PgkcL^V^Z^G^U^V^X(;^E^[^BIP^ZgdlgD2^W
^X^C3^A,^BH[H_GBkkjbN/^K^L^C^H^O$^X^
^^CCYI)\FDgachH=^Q^O^R^N’<^M^_^LJPIXCFcogoC.^N^X^M’ ^X^B ^NHTG^DMnaeeF;^^
^X^U9^Z
^^D[A[N@ibcbH5^X^K^L^@3^O^LIVNTJFfhcL ^M CYI\NHebcC:^
^T^@^NIVN4YXIFlglL#^Y^W^Y^M(^V^QHQHQHBhdohA$^\ ,^E^D^X.^Z^ZGTDQAGafmaH/^O^M^K^W6^Z^G^S^WIVNTJFfhcL7^K^K5
^F N^J_IFlglL+^H^P^X?^M^H^\HQHQHBhdohA&^E^_^W:^C^E^Y^DHTG^DMnaeeF'^Z^N^D%^E^A^WL\CSI@anacI8^D^Y^K^QL\CSI@anacI) ^O^M^N;^VCYI\NHebcC^X_GTDUAGafmaH^YXCPLULJlib$

3.- but when I try to decrypt data with :
./xorknown.py ./crypted.txt ‘{“statusDWash”:{"StatoWiFi’ 16
I get :
The length of the known plaintext is greater than max_key_length (=21). Please give a smaller plaintext or incrase max_key_length.

Can someone help me to solve it?

At the step 3 use a shorter key, for example: ./xorknown.py ./crypted.txt ‘{"StatoWiFi’ 16

I used :
./xorknown.py ./crypted.txt ‘{"StatoWiFi’ 16
./xorknown.py ./crypted.txt ‘{"statusDWash’ 16
./xorknown.py ./crypted.txt ‘{"status’ 16
./xorknown.py ./crypted.txt ‘{"Status’ 16

and got :
Searching XOR-encrypted ./crypted.txt for string ‘{"Status’ (max_key_length = 16)

but nothing else…