Dishwasher - Candy simply FI - CDI 6015 WiFi

I tried with the files from GitHub - MelvinGr/CandySimplyFi-tool: Tool for getting data of your Candy Simply-Fi devices :
on a debian 11.3:

~$ ./simplyfi
./simplyfi: line 7: erreur de syntaxe près du symbole inattendu « newline »
./simplyfi: ligne 7: `<!DOCTYPE html>'

I have a dish washer RDIN 4S622PS-47 and a washing machine OBWS69TWMCE-47
they are both connected to the wifi and are running with the epicure android app.

I’ve just got a Candy H9A2TE-80 Tumble Dryer that uses the hOn app.
It appears that it no longer connects directly to the device itself and instead goes via a cloud service.

You can get some information about the device and what it’s doing by polling some of these API’s, but it appears that it may also be using Amazon MQTT to send events too.

I’ve had a go at trying to find credentials - and it appears its using Salesforce SSO with Amazon Cognito. So there’s a fair bit going on. I wasn’t able to find credentials for the MQTT topics.

I’m fairly green at reverse engineering so if anyone with any knowledge wants to collaborate, please let me know!! :smiley:

I’m gonna keep on it and may start a new thread with anything useful i find.

I have the same problem with oven. It use hOn and I can’t connect with it.

Any advances with hOn devices?

Are you sure that use esp? maybe we can flash a custom firmware.

My current understanding is that the devices communicate via MQTT with an AWS.IOT endpoint (a30f6tqw0oh1x0-ats.iot.eu-west-1.amazonaws.com).
The app then pulls all it’s info from an API.

However, there’s also a “cognito-token” in the headers sent to the server, and in one of the API calls it shows the MQTT topics. But everything i’ve tried to connect to the MQTT server fails.
I’m at a dead end.

Hi all - really impressed with the work that’s gone in to reverse engineering this!

I’ve got three Candy appliances: dishwasher, drier, washing machine. I added the dishwasher and drier fine using this integration, but I can’t seem to add the washing machine. I get the “Failed to detect encryption, check logs” error (log below).

I’ve used Melvin’s CandySimplyFi-tool which gives me a key from the washing machine, but I don’t know where to use it? The config flow doesn’t prompt me for a key, and if I add the MAC address and key in my configuration.yaml like the example earlier in the thread, it also gives an error.

Any ideas what I’m doing wrong?

Traceback (most recent call last): 
File "/config/custom_components/candy/client/__init__.py", line 62, in detect_encryption assert resp_json.get("response") != "BAD REQUEST" 
AssertionError 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
File "/config/custom_components/candy/config_flow.py", line 45, in async_step_user encryption_type, key = await detect_encryption( 
File "/config/custom_components/candy/client/__init__.py", line 69, in detect_encryption async with session.get(url) as resp: 
File "/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 559, in _request await resp.start(conn) 
File "/usr/local/lib/python3.9/site-packages/aiohttp/client_reqrep.py", line 898, in start message, payload = await protocol.read() # type: ignore[union-attr] 
File "/usr/local/lib/python3.9/site-packages/aiohttp/streams.py", line 616, in read await self._waiter 
aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected

Agree with you andrey1 - the handling of the device state when it’s off could be better. Not sure what to suggest as I see this issue with devices on some of the in-built integrations. Feels wrong that ‘powered off’ goes to state Unavailable and HA then hides the integration thinking the device is dead.

Hi!

I am working in API using only Rest. For now, I get some data

1 Like

This is fantastic. I’ll be following your progress as I’d love hOn integration for my dishwasher.

1 Like

Hi, do you use virtual output with http commands in Loxone?

Hi, first thanks to all of you that are trying to help the community :slight_smile:
I have a Hoover Tumbler dryer, but whenever I try to “talk” to her, she answers “ERR_CONNECTION_REFUSED”. I tryed the “curl” think pointing to its IP, but no answer. I thought it was my network, but now it should be nicely configured, and still…nothing :frowning:

If anyone managed to do this, I would appreciate it :slight_smile:

in binary mode file transfert, it works much better…

shame on me…

I tried to connect my Rosières Dishwasher RDIN 4S622PS-47 (Rosières is a brand of Hoover/Candy and works with the Android app)

http://192.168.92.214/http-read.json?encrypted=0
{"response":"BAD REQUEST"}

So I tried in Encrypted method:

http://192.168.92.214/http-read.json?encrypted=1
186A646C4F1D1000101D122D320C1504415D156867676D43371C001D0A3A0F2A0A4554475D4C486C6E61684B2602020500022B171F0116044652432C554F4A61696E67473E1A05150B2C36081605445641554C4960646D684625041D042E071E0A040147574C544348656B606C4F351802151A36190114435E4A514B49606C656A453A1708070A340A074353475D44406E6D676C4F2B070E4652435947416B666A6E4C351F01031305054353473D5E4E4F6A646C644C2119101A002D1714445641574C4960646D684627110C0B29090311281E114F54465046446C636C644428060B0F1C3E1A0513104A5B4B554F4A61696E67473F0B09350D05044B5F4F545C5645426867676D432901121A360C0A18415D4C554F42696B6D6143240C1E153E0A091D004F54465146446C636C64442313020021020116435E4A504B49606C656A453C001E0B10435E4A514B49606C656A452D0D080D0F34144A5B4B554F4A61696E67471F5F465B4659434568676F6541155C47574C544348656B606C4F145F415D4C564F42696B6D61431B514F5C4E5245426867676D43165D4353475D44406E6D676C4F1C52435E4A504B49606C656A451C524F54465446446C636C64441E5B4554475C4C486C6E61684B1754445641293B29214C486C6E61684B175C534E59455F4760646D1C69621C

Good ! I ask for the key: :+1:

User@Debian-11:~$ ./simplyfi 192.168.92.214 getkey
## Candy Simply-Fi tool by Melvin Groenendaal ##
error: find_xor_key_list, could not find key

What’s wrong ??? the simplyfi file works on another device… ???

Hi Stephen, can you share the commands you are using in loxone ?

Since this forum is more for Home Assistant I will send you to the Home Automation forum where I have just posted a reply under their Loxone forum.

Could You as well help me?
I put “http://192.168.1.0/http-read.json?encrypted=1 " (in my router i found name ESP_FB37BC for 192.168.1.0” and get the code:
484A44537B3D23043C254B7B1B1A0930412E2D3F7B742C68425931152D052E2D60332F2E2C3D755F6A611A1B7766614D11023C287B7475577D651A1B7766614D110A2F3931033347727209155661624D3A651E287B7475547D72143A7065616663351E327B7475556A7C353D73654A177F22383F356C6D477A72143A706561666722232A7B74755C7872143A70656166603727340A3E755F6A641A1B7766614D11083E2E686C6D477872143A706561667C373A687B7475556A7C353D73654A0B43337D78636C6747645D323E734E273447736C607B7E7549455A313E5823183006657478696C7B6842593115351C1C72117D6C6A7B625A6F41591A780A185F6609657E7875435D6C417277470E544A7E11776C7654445E6C6A034C521B014A7E11776C7654445E6C6A144A4E2E4E52660365625753475E470C3554611B004A7E11757B6F7B625A6F41591A651F013C2D5E226C607B7F64546A7C353D73654A165624272A3C073347727208155661624D3A650D323C2D3C3038034C560E094A7E11776C5753472A68422D
but i have problem with ./simplyfi getkey
While trying to just put IP in HA add’on from HACS i get: “Failed to detect encryption, check logs”

{
"statusLavatrice":{
"WiFiStatus":"0",
"Err":"0",   ERRORI
"MachMd":"1", STATO LAVATRICE
"Pr":"1",  PROGRAMMA
"PrPh":"0", STATO
"PrCode":"136", PROGRAMMA IN CORSO
"SLevel":"0", LIVELLO VAPORE
"Temp":"30", TEMPERATURA IMPOSTATA
"SpinSp":"4", VELOCITA CENTRIFUGA X100
"Opt1":"0", PRELAVAGGIO
"Opt2":"0", IGIENE +
"Opt3":"0", ANTIPIEGA
"Opt4":"0", BUONANOTTE
"Opt5":"0", RISCIAQUO X1
"Opt6":"0", RISCIAQUO X2
"Opt7":"0", RISCIAQUO X3
"Opt8":"0", ACQUA PLUS
"Opt9":"0", SCONOSCIUTO
"Steam":"0", VAPORE 5 LIVELLI 1 2 3 4 5
"DryT":"0", ASCIUGATURA?
"DelVal":"29", TIMER IMPOSTATO
"RemTime":"2340", TEMPO FINE PROGRAMMA
"RecipeId":"0", RICETTA PULIZIA?
"Lang":"0", LINGUA IMPOSTATA?
"FillR":"0", RIEMPIMENTO FILTRO?
"DisTestOn":"0", DIAGNOSI
"DisTestRes":"0",DIAGNOSI
"CheckUpState":"0", DIAGNOSI
"T0W":"0", CONSUMI?
"TIW":"0",CONSUMI?
"T0R":"0",CONSUMI?
"numF":"0",
"unbF":"0",
"unbC":"0",
"NtcW":"136", TEMPERATURA INTERNA
"NtcD":"7",  6 porta chiusa, porta 10 aperta, 7 bloccato?
"motS":"0", GIRI MOTORE PWM
"APSoff":"0",
"APSfreq":"61967",
"chartL":"0"
}

my package for washer

https://github.com/riddik14/CandySimplyFi-hassio-package based on this page and github

tested on candy model CANRO1486DWMCE1S

homeassistant:
  customize:
    package.node_anchors:
      customize: &customize
        package: 'candy'
        author: 'domenico ceccarelli'
        reference: 'riddik14'

      expose: &expose
        <<: *customize
        haaska_hidden: false
        homebridge_hidden: false
automation:
  - alias: avviso avvio ritardato lavatrice
    description: ''
    trigger:
    trigger:
    - platform: numeric_state
      entity_id:
      - sensor.candy_wifi
      attribute: DelVal
      above: '1'
    condition: []
    action:
    - delay:
        hours: 0
        minutes: 0
        seconds: 5
        milliseconds: 0
    - service: script.my_notify
      data_template:
        call_no_annuncio: 1
        alexa: 1
        google: 0
        title: Lavatrice
        message:        
                         la lavatrice si avvierà tra {{((states.sensor.candy_wifi.attributes.RemTime | int(0)) / 60) | round(0) }} di minuti. 
                        hai selezionato il Programma  {{ states.sensor.candy_wifi_programma.state }} ,  
                        avvio il ciclo di lavaggio, con temperatura a {{ (states.sensor.candy_wifi_temp.state) }} gradi, 
                        con centrifuga a {{ (states.sensor.candy_wifi_centrifuga.state) }} giri 
                 
    mode: single
  - alias: avviso Programma lavatrice
    description: ''
    trigger:
    trigger:
    - platform: state
      entity_id:
      - sensor.candy_wifi
      attribute: MachMd
      from: '1'
      to: '2'
    condition: []
    action:
    - delay:
        hours: 0
        minutes: 0
        seconds: 5
        milliseconds: 0
    - service: script.my_notify
      data_template:
        call_no_annuncio: 1
        alexa: 1
        google: 0
        title: Lavatrice
        message: >
                  {% if  ((states.sensor.candy_wifi.attributes.DelVal) | int(0) > 0) %}
          
                       "ritardo avvio impostato {{ (states.sensor.candy_wifi.attributes.DelVal) }} di minuti. 
                        hai selezionato il Programma  {{ states.sensor.candy_wifi_programma.state }} ,  
                        avvio il ciclo di lavaggio, con temperatura a {{ (states.sensor.candy_wifi_temp.state) }}gradi, 
                        con centrifuga a {{ (states.sensor.candy_wifi_centrifuga.state) }} giri "
                  {% else %}
                       "hai selezionato il Programma  {{ states.sensor.candy_wifi_programma.state }} ,  
                        avvio il ciclo di lavaggio, con temperatura a {{ (states.sensor.candy_wifi_temp.state) }} gradi, 
                        con centrifuga a {{ (states.sensor.candy_wifi_centrifuga.state) }} giri.
                        tempo Previsto alla fine  {{ ( states.sensor.candy_wifi_termine_programma.attributes.Termine) }} minuti. "
                  {% endif %}
    mode: single
  - alias: lavatrice inizio lavaggio
    description: ''
    trigger:
    - platform: state
      entity_id:
      - sensor.candy_wifi
      attribute: MachMd
      to: '2'
    condition: []
    action:
    - service: notify.telegram
      data:
        message: la lavatrice ha iniziato il ciclo di lavaggio alle ore {{ states.sensor.time.state
          }}
    mode: single
  - alias: lavatrice fine lavaggio
    description: ''
    trigger:
    - platform: state
      entity_id:
      - sensor.candy_wifi
      attribute: MachMd
      to: '7'
    condition: []
    action:
    - data:
        entity_id: media_player.tutti
        message: la lavatrice ha finito
      service: tts.google_translate_say
    - service: notify.telegram
      data:
        message: la lavatrice ha finito il ciclo di lavaggio alle ore {{ states.sensor.time.state
          }}
    - service: script.my_notify
      data_template:
        call_no_annuncio: 1
        alexa: 1
        google: 0
        title: Lavatrice
        message: La lavatrice ha finito
    mode: single


appdaemon
folder apps

in file apps.yaml add line

candy_washing_machine:
  module: candy-washing-machine
  class: CandyWashingMachine
  encryption_key: "your_encrypted_key"
  appliance_host: "192.168.1.yourip"

in app folder create file named candy-washing-machine.py and paste this

# based on https://github.com/jezcooke/haier_appdaemon/blob/main/checkappliance.py
import hassapi as hass
import requests
import json
import codecs
from datetime import datetime, timedelta, timezone

appliance_entity = "sensor.candy_wifi" # The name of the entity to use/create in Home Assistant (value with '_stats' appended)
status_root = "statusLavatrice"             # The root level JSON element returned by 'http-read'
power_attribute = "MachMd"                  # The name of the JSON attribute that containes the power on/off state.
stats_root = "statusCounters"               # The root level JSON element returned by 'http-getStatistics'
polling_interval = 30                       # How frequently check for the latest status.
request_timeout =  15                        # Request timeout should be less than the polling interval 




max_retry_before_unavailable = 5


class CandyWashingMachine(hass.Hass):
    def initialize(self):
        self.retry = 0
        self.previous_end = None
        self.run_every(self.check_appliance, "now", polling_interval)
        self.encryption_key = self.args["encryption_key"]
        self.log(f"encryption_key: {self.encryption_key!r}")
        self.appliance_host = self.args["appliance_host"]

    def check_appliance(self, kwargs):
        try:
            status = self.get_status()
            power = int(status[status_root][power_attribute])
            if power == 1:
                state = "Ferma"
            elif power == 2:
                state = "In lavaggio"
            elif power == 3:
                state = "In Pausa"
            elif power == 4:
                state = "Partenza ritardata selezionata"
            elif power == 5:
                state = "Partenza ritardata"
            elif power == 6:
                state = "ERRORE"
            elif power == 7:
                state = "Finito"
            elif power == 8:
                state = "Finito"
            else:
                state = "Sconosciuto"
            
            
            
            
            
            
            attributes = status[status_root]
            self.set_state(appliance_entity, state=state, attributes=attributes) #{"friendly_name": "Candy Lavatrice", "icon": "mdi:washing-machine" })
            self.retry = 0
            remaining_minutes = int(attributes["RemTime"]) // 60 + int(attributes["DelVal"])
            now_rounded = datetime.now(timezone.utc).replace(second=0, microsecond=0) + timedelta(minutes=1)
            end = now_rounded + timedelta(minutes=remaining_minutes)
            if self.previous_end is not None:
                if abs(end - self.previous_end) <= timedelta(minutes=1):
                    end = max(end, self.previous_end)
            self.previous_end = end
            entity_id = appliance_entity + "_termine_programma"
            self.set_state(
                entity_id,
                state=end.isoformat(),
                attributes={"friendly_name": "Candy Fine ","device_class": "timestamp","Termine": remaining_minutes, "icon": "mdi:av-timer"},
            )
        except Exception as e:
            self.log(f"error when getting status: {e}")
            self.retry += 1
            if self.retry > max_retry_before_unavailable:
                self.set_state(appliance_entity, state="OFF LINE")
                entity_id = appliance_entity + "_termine_programma"
                self.set_state(
                    entity_id,
                    state="unavailable",
                    attributes={"friendly_name": "#Candy Lavatrice", "device_class": "timestamp", "icon": "mdi:timer-off-outline"},
                )
                previous_end = None
            return
        
#cicli conta
        try:
            entity_id = appliance_entity + "_stats"
            stats = self.get_stats()[stats_root]
            total = 0
            for (key, value) in stats.items():
                if key.startswith("Program"):
                    total += int(value)
            self.set_state(entity_id, state=total, 
                        attributes={ "friendly_name": "Candy Cicli totali", "icon": "mdi:washing-machine"},)
        except:
            pass
######################################
#programma
        try:
            status = self.get_status()
            programm = int(status[status_root]["PrCode"])
            rem = int(status[status_root]["RemTime"]) // 60
            
            if programm == 136:
                state_program = "SPECIAL 39 MINUTI"
            elif programm == 135:
                state_program = "MISTI & COLORATI 59  MINUTI"
            elif programm == 8:
                state_program = "COTONE PERFETTO" 
            elif programm == 40:
                state_program = "IGIENE PLUS"
            elif programm == 72:
                state_program = "SPORT PLUS 39 MINUTI"
            elif programm == 4:
                state_program = "DELICATI 59 MINUTI"
            elif programm == 7 and rem == 14:
                state_program = "RAPIDO 14 MINUTI"
            elif programm == 7 and rem == 30:
                state_program = "RAPIDO 30 MINUTI"
            elif programm == 7 and rem == 45:
                state_program = "RAPIDO 45 MINUTI"
            elif programm == 39:
                state_program = "RAPIDO 15 MINUTI"
            elif programm == 35:
                state_program = "RISCIACQUI"
            elif programm == 129:
                state_program = "SCARICO E CENTRIFUGA"
            elif programm == 5:
                state_program = "LANA A MANO"
            elif programm == 3:
                state_program = "SINTETICI E COLORATI"
            elif programm == 11:
                state_program = "LAVAGGIO 20°"
            elif programm == 2:
                state_program = "ECO 40° - 60°"
            elif programm == 65:
                state_program = "COTONE"
            else:
                state_program = programm
            
            entity_id = appliance_entity + "_programma"
            self.set_state(entity_id, state=state_program, attributes = {"friendly_name": "Programma", "icon":"mdi:format-list-bulleted-type"})
            #self.retry = 0
        except:
            pass
    ###############    
#errori
        try:
            status = self.get_status()
            error_code = int(status[status_root]["Err"])
            if error_code == 1:
                state_error = "E1"
            elif error_code == 2:
                state_error = "E2"
            elif error_code == 3:
                state_error = "E3"
            elif error_code == 4:
                state_error = "E4"
            elif error_code == 5:
                state_error = "E5"
            elif error_code == 6:
                state_error = "E6"
            elif error_code == 7:
                state_error = "E7"
            elif error_code == 8:
                state_error = "E8"
            elif error_code == 9:
                state_error = "E9"
            elif error_code == 10:
                state_error = "E10"
            elif error_code == 11:
                state_error = "E11"
            elif error_code == 12:
                state_error = "E12"
            elif error_code == 13:
                state_error = "E13"
            elif error_code == 14:
                state_error = "E14"
            elif error_code == 15:
                state_error = "E15"
            elif error_code == 16:
                state_error = "E16"
            elif error_code == 17:
                state_error = "E17"
            elif error_code == 18:
                state_error = "E18"
            elif error_code == 19:
                state_error = "E19"
            elif error_code == 20:
                state_error = "E20"
            elif error_code == 21:
                state_error = "E21"
            else:
                state_error = "---"
            
            
            
            entity_id = appliance_entity + "_errore"
            self.set_state(entity_id, state=state_error, attributes = {"friendly_name": "ERRORI", "icon":"mdi:alert-circle-outline"})
        #    self.retry = 0
        except:
            pass
    ###############    
#temp impostata
        try:
            status = self.get_status()
            temp = int(status[status_root]["Temp"])

            entity_id = appliance_entity + "_temp"
            self.set_state(entity_id, state=temp, attributes = {"friendly_name": "Temperatura impostata", "unit_of_measurement": "°C"})
        #    self.retry = 0
        except:
            pass
    ###############    
#temp interna
        try:
            status = self.get_status()
            temp1 = int(status[status_root]["NtcW"]) / 10

            entity_id = appliance_entity + "_temp_interna"
            self.set_state(entity_id, state=temp1, attributes = {"friendly_name": "Temperatura attuale", "unit_of_measurement": "°C"})
        #    self.retry = 0
        except:
            pass
    ###############           
    ###############  
#counter
        try:
            statusmv = self.get_stats()
            mov = int(statusmv[stats_root]["CounterMV"])
            mov = 0
            entity_id = appliance_entity + "_countmove"
            self.set_state(entity_id, state=mov, attributes = {"friendly_name": "Conta Movimenti", "icon":"mdi:vibrate"})
        #    self.retry = 0
        except:
            pass
    ###############           
        try:
            statusfill = self.get_status()
            fill = int(statusfill[status_root]["FillR"])
            fill = 0
            entity_id = appliance_entity + "_riempimento"
            self.set_state(entity_id, state=fill, attributes = {"friendly_name": "Percentuale riempimento", "icon":"mdi:waves-arrow-up", "unit_of_measurement": "%"}) 
        #    self.retry = 0
        except:
            pass
    ###############    
#centrifuga impostata
        try:
            statusrpm = self.get_status()
            rpm = int(statusrpm[status_root]["SpinSp"]) * 100

            entity_id = appliance_entity + "_centrifuga"
            self.set_state(entity_id, state=rpm, attributes = {"friendly_name": "Giri Centrifuga", "unit_of_measurement": "rpm", "icon":"mdi:sync"})
            self.retry = 0
        except:
            pass
    ###############  
    ###############     
#motore
        try:
            status = self.get_status()
            value = int(status[status_root]["motS"]) / 10 

            entity_id = appliance_entity + "_motore"
            self.set_state(entity_id, state=value, 
                        attributes = {"friendly_name": "Giri Motore", "unit_of_measurement": "rpm", "icon": "mdi:engine"})
        #    self.retry = 0
        except:
            pass
#prelavaggio
        try:
            status = self.get_status()
            prelavax = int(status[status_root]["Opt1"])
            if prelavax == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt1"
            self.set_state(entity_id, state=state, attributes = {"friendly_name": "Prelavaggio", "icon":"mdi:hand-wash"})
        #    self.retry = 0
        except:
            pass
    ###############     
#igiene 
        try:
            status = self.get_status()
            igieneplus = int(status[status_root]["Opt2"])
            if igieneplus == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt2"
            self.set_state(entity_id, state=state, attributes = {"friendly_name": "Igiene +", "icon":"mdi:hospital-box"})
        #    self.retry = 0
        except:
            pass
    ###############  
#antipiega
        try:
            status = self.get_status()
            valueantip = int(status[status_root]["Opt3"])
            if valueantip == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt3"
            self.set_state(entity_id, state=state, attributes = {"friendly_name": "Antipiega", "icon":"mdi:tshirt-v"})
        #    self.retry = 0
        except:
            pass
    ###############            
#buonanotte
        try:
            status = self.get_status()
            value = int(status[status_root]["Opt4"])
            if value == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt4"
            self.set_state(entity_id, state=state, attributes = {"friendly_name": "Buonanotte", "icon":"mdi:weather-night"})
        #    self.retry = 0
        except:
            pass
#acqplus
        try:
            status = self.get_status()
            valueacq = int(status[status_root]["Opt8"])
            if valueacq == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt8"
            self.set_state(entity_id, state=state, attributes = {"friendly_name": "Acquaplus", "icon":"mdi:water-plus"})
        #    self.retry = 0
        except:
            pass
    ###############     
#option9 
        try:
            status = self.get_status()
            value = int(status[status_root]["Opt9"])
            if value == 1:
                state = "ON"
            else:
                state = "OFF"

            entity_id = appliance_entity + "_opt9"
            self.set_state( entity_id, state=state, attributes = {"friendly_name": "Opzione 9 sconosciuta"})
        #    self.retry = 0
        except:
            pass   
#vapore
        try:
            status = self.get_status()
            valuevap = int(status[status_root]["Steam"])
            if valuevap == 1:
                state = "Basso"
            elif valuevap == 2:
                state = "Medio Basso"
            elif valuevap == 3:
                state = "Medio"
            elif valuevap == 4:
                state = "Medio Alto"
            elif valuevap == 5:
                state = "Massimo"
            else:
                state = "ESCLUSO"

            entity_id = appliance_entity + "_vapore"
            self.set_state( entity_id, state=state, attributes = {"friendly_name": "Vapore", "icon":"mdi:cloud-outline"})
        #    self.retry = 0
        except:
            pass
#stato lavaggio
        try:
            status = self.get_status()
            macchine = int(status[status_root]["PrPh"])
            #macchine = 0
            if macchine == 1:
                statemd = "In prelavaggio"
            elif macchine == 2:
                statemd = "In lavaggio"
            elif macchine == 3:
                statemd = "Risciacquo"
            elif macchine == 4:
                statemd = "Ultimo Risciacquo"
            elif macchine == 5:
                statemd = "Fine"
            elif macchine == 6:
                statemd = "Asciugatura"
            elif macchine == 7:
                statemd = "ERRORE"
            elif macchine == 8:
                statemd = "Vapore"
            elif macchine == 9:
                statemd = "Centrifuga Notturna"
            elif macchine == 10:
                statemd = "Centrifuga"
            else:
                statemd = "Inattiva"

            entity_id = appliance_entity + "_stato_lavatrice"
            self.set_state( entity_id, state=statemd, attributes = {"friendly_name": "Candy Stato", "icon":"mdi:washing-machine"})
            self.retry = 0
        except:
            pass
#livello sporco
        try:
            statussp = self.get_status()
            sporco = int(statussp[status_root]["SLevel"])
            #sporco = 0
            if sporco == 1:
                statesp = "Poco"
            elif sporco == 2:
                statesp = "Normale"
            elif sporco == 3:
                statesp = "Molto"
            else:
                statesp = "ESCLUSO"

            entity_id = appliance_entity + "_livello_sporco"
            self.set_state( entity_id, state=statesp, attributes = {"friendly_name": "Livello di Sporco", "icon":"mdi:car-brake-fluid-level"})
        except:
            pass
#controllo remoto
        try:
            status = self.get_status()
            valueremoto = int(status[status_root]["WiFiStatus"])
            
            if valueremoto == 1:
                controllorem = "ON"
            else:
                controllorem = "OFF"

            entity_id = appliance_entity + "_wifi_2"
            self.set_state( entity_id, state=controllorem, 
                            attributes = {"friendly_name": "Controllo Remoto", "WiFiStatus": valueremoto , "icon":"mdi:wifi-cog"})
            self.retry = 0
        except:
            pass
#filtro
        try: 
            statsf = self.get_stats()[stats_root]
            totalef = 0
            for (key, value) in statsf.items():
                if key.startswith("Program"):
                    totalef += int(value)
            filtro_lav = 100 - totalef
            if filtro_lav < 1:
                statefiltro = "Da Pulire"
            elif filtro_lav < 70:
                statef = "Medio Sporco"
            elif filtro_lav < 40:
                statefiltro = "Sporco"
            else:
                statefiltro = "Pulito"
                
            entity_id = appliance_entity + "_filtro"
            self.set_state(entity_id, state=statefiltro, 
                        attributes = {"friendly_name": "Filtro", "Intasamento": totalef , "icon":"mdi:air-filter"})
        except:
            pass
#filtro calcare
        try: 
            statsfc = self.get_stats()[stats_root]
            totalefc = 0
            for (key, value) in statsfc.items():
                if key.startswith("Program"):
                    totalefc += int(value)
            filtro_lav_c = 105 - totalefc
            if filtro_lav_c < 1:
                statefiltroc = "Da Pulire"
            elif filtro_lav_c < 70:
                statefiltroc = "Medio Sporco"
            elif filtro_lav_c < 40:
                statefiltroc = "Sporco"
            else:
                statefiltroc = "Pulito"
                
            entity_id = appliance_entity + "_filtro_calcare"
            self.set_state(entity_id, state=statefiltroc, attributes = {"friendly_name": "Filtro Calcare", "Livello": filtro_lav_c , "icon":"mdi:air-filter"})
        except:
            pass
####################################
#risciacquo
        try:
            status = self.get_status()
            opta = int(status[status_root]["Opt5"])
            optb = int(status[status_root]["Opt6"])
            optc = int(status[status_root]["Opt7"])
            #macchine = 0
            if opta == 1:
                state_risc = "X 1"
            elif optb == 1:
                state_risc = "X 2"
            elif optc == 1:
                state_risc = "X 3"
            else:
                state_risc = "OFF"

            entity_id = appliance_entity + "_risciacquo"
            self.set_state( entity_id, state=state_risc, attributes = {"friendly_name": "Risciaquo", "icon":"mdi:water"})
            self.retry = 0

        except:
            pass
####################################
#diagnosi
        try:
            status = self.get_status()
            diaa = int(status[status_root]["DisTestOn"])
            diab = int(status[status_root]["DisTestRes"])
            #macchine = 0
            if diaa == 1:
                state_risc = "In Corso"
            elif diab == 1:
                state_risc = "OK"
            elif diab == 2:
                state_risc = "ERRORE"
            else:
                state_risc = "---"

            entity_id = appliance_entity + "_diagnosi"
            self.set_state( entity_id, state=state_risc, attributes = {"friendly_name": "Diagnosi", "icon":"mdi:medical-bag"})
            self.retry = 0
        except:
            pass
##################################
##################################
    def get_status(self):
        return self.get_data("read")

    def get_stats(self):
        self.get_data("prepareStatistics")
        return self.get_data("getStatistics")

    def get_data(self, command):
        res = requests.get(
            "http://" + self.appliance_host + "/http-" + command + ".json?encrypted=1",
            timeout=request_timeout,
        )
        return json.loads(self.decrypt(codecs.decode(res.text, "hex"), self.encryption_key))

    def decrypt(self, cipher_text, key):
        decrypted = ""

        for i in range(len(cipher_text)):
            decrypted += chr(cipher_text[i] ^ ord(key[i % len(key)]))

        return decrypted

Hi maybe im late but im also found my encryption key in simply fy app sql database

So try the following:
1.Install simply fy from playstore
2.Pair your device
If you have rooted device:
3.Open /data/data/it.candy.simplyfi/datbases/candy_database.sql with an sql lite editor
4.Your encryption key will be in configured_appliances table under the colum encryption key
If you dont have rooted device:(maybe install a rooted bluestacks?)
3. try: ‘adb backup -f simplyfi.ab -apk it.candy.simplyfi’
4. Get Android Backup Extractor and extract the backup file
Link:Release master-20220809063558-8fdfc5e: Merge pull request #101 from nelenkov/renovate/gradle-7.x · nelenkov/android-backup-extractor · GitHub
Command: ‘java.exe -jar abe.jar unpack simplyfi.ab simplyfy.tar’
5 Unpack simplyfy.tar tar archive with the program of your choice
6.Open candy_database.sql with an sql lite editor
7.Your encryption key will be in configured_appliances table under the colum encryption key

Great info it was very very useful to me
i have a Candy:ROW4856DWMCE/1-S
I did some digging in the Candy Simplyfy database files :grinning:
Especially in /data/data/it.candy.simplyfi/datbases/candy_database.sql
i not only found my encryption key there(answear 238)
But i managed to find my default program paramaters too
My machine have 23 default programs
See:PrNm and PrCodes.csv
Minimal code to start a program(eg Hygane 60):
http://{washing machine ip}/http-write.json?encrypted=0&data=Write=1&StSt=1&PrNm=17&PrCode=161
Get PrNm and PrCode from ‘PrNm and PrCodes.csv’ or use the sql quarry in the csv first line to list your standard programs manually

My Simply Candy app have around 60 programs so it is still a lot of digging to do :sweat_smile:
I guess the other programs are variations of the default programs

All of the parameters can be found for this programs in:All-params.csv
Google drive link: Candy Wifi - Google Drive

Other usefull staff:
Candy Simplyfy app can send data to the washing machine via LAN and via Candy Servers
You want it to send it via LAN because
1.Its way more faster
2.And you can spoof the traffic
The app will only communicate via lan if you grant approipriate permissions to it gprs and maybe read conntacts or read phone status i dont know

How to spoof traffic
I used arpspoof tool (https://github.com/alandau/arpspoof) to do some ARP poisoning (redirect traffic from Candy Simplfy app to my pc)
and I also used whireshark to listen to the redirected traffic
Traffic decryption is vastly discussed in this tread

if you use StSt=0 in the request and you also set the DelVl to anything other then 0 remote control will crash

1 Like

Hi everyone

I have a Candy clothes washer model GVFWFL4139WHR-12.

I have downloaded the Candy SimplyFi addon from HACS, but when I want to add the integration, I receive the following error: “Failed to detect encryption”.
Candy 1

I have the correct IP, it responds well to the sentence:
http://IP/http-read.json?encrypted=1

With encrypted=0 I receive as response
{“response”:“BAD REQUEST”}

Trying to decode the response received with “encryption=1” on the website https://www.online-python.com/pm93n5Sqg4 I get no response.

I’m lost, I don’t know where the error is.

Can any one guide me how to follow or what to modify?
The integration never asks me for the KEY, I did not read this error in the entire thread.

Thank you very much

http://192.x.y.z/http-read.json?encrypted=1

116F626D4E18190A101412290D100F11180B0B014E5116666E6868473B0F280C3916091019184F5146504349616C676C48271A164E514F5951544349616C676C482F0907042609495E435347406B646C634038164E514F5A56434D68666F67473A10380C4E514F5D464D6C6F656F4C3626071E010049574956545447406B646C63403C01011B4F51465354504E4A636F636B4A371C02033814435B475E535B47466F626D6549221B1050435F4E564C496768616D4E241D1F56435B475C444268606B6146231B1958465B43554E4A636F636B4A2B1C1F59495E435147406B646C63402714185E4F5146514349616C676C482D18105A49574954434D68666F674725121C534E514F5B464D6C6F656F4C2A1A16504656495D49486C6B6C65443D110F03054656495D49486C6B6C65442A1713364A5E4E5F4F47696B686C4E220B093C03044656495F5E51434D68666F674738070530050608495E435047406B646C63403A010F021D0E2D05435F4E564C496768616D4E28050E070A34153F120F110F4052465C4960616D1C6C6F11

3 Likes