Closed BenGlossop09 closed 2 months ago
Excellent, thank you for the program codes. Ill create another configuration.yaml to match the settings for your machine.
My notes suggest I added line 54 for testing. You can either comment this line out:
if k == 'PrNm': k == 'Pr'
Or edit your .yaml command_line sensor attribute Pr instead of PrNm and the template back to:
- sensor:
- name: 'Washer/Dryer - Program'
icon: mdi:progress-star
state: >
{% set Prog = state_attr('sensor.candy_washer_dryer', 'Pr') %}
#etc
If you have no luck there are 2 other hoover/ candy integrations on HACS that are more robust than mine. I created this project because neither one worked for my machine at the time. As my first Python project its very much just muddled together in the simplest way possible. It worked so I have not checked the others recently but they are still under development. Candy Simpy-Fi Haier hOn Although I use the Hoover Wizard app both of the above use the same hardware so should work in theory.
I managed to get it working! I changed all references of 'Pr' to 'PrNm' and reloaded all yaml (in developer tools). I also have had no luck with Haier hOn. Candy Simpy-Fi works well for me but your solution gives far more information. configuration.yaml:
command_line:
- sensor:
name: 'Candy Washer Dryer'
scan_interval: 60
command_timeout: 30
command: python3 ./pyscript/candy.py
value_template: '{{ value_json }}'
json_attributes:
- WiFiStatus
- Err
- MachMd
- PrNm
- PrPh
- Temp
- SpinSp
- RemTime
- DryT
- DelVal
- TotalTime
- sensor:
- name: 'Washer/Dryer - Program'
icon: mdi:progress-star
state: >
{% set Prog = state_attr('sensor.candy_washer_dryer', 'PrNm') %}
and candy.py
url = 'http://192.168.4.38/http-read.json?encrypted=1'
key = 'mnlkbhjdlbelcgkc'
request_timeout = 10
retries = 3
retry_delay = 2
candyOff = {'WiFiStatus':'1', 'Err':None, 'MachMd':None, 'PrNm':None, 'PrPh':None, 'Temp':None, 'SpinSp':None, 'RemTime':'0', 'DryT':'0', 'DelVal':0, 'TotalTime':'0'}
tries = 0
candy = {}
Further changes I have made; Changed default 'No Error' code to 0 - 0 is no error for my machine. My machine outputs remaining time in seconds so I have added a divide by 60 to Remaining Time and Total Time. Note remaining time shows as xx.x instead of xx minutes, I should add a truncation to solve this.
Outstanding issues; Not much of an issue but the only feature not supported in the PrNm list is the WiFi setting. It doesn't appear to hold a unique value. I have not yet tested what the program entity will output when WiFi is selected and a wash is started - I would assume the machine will output the actual program selected. Rapid 14'/30'/44' do not each hold a unique program so are bundled together. Add commands?
Full candy.py for my machine:
#! /usr/bin/env python
import requests
import json
import time
url = 'http://192.168.X.XX/http-read.json?encrypted=1'
key = 'ENCRYPTION KEY HERE'
request_timeout = 10
retries = 3
retry_delay = 2
candyOff = {'WiFiStatus':'1', 'Err':None, 'MachMd':None, 'PrNm':None, 'PrPh':None, 'Temp':None, 'SpinSp':None, 'RemTime':'0', 'DryT':'0', 'DelVal':0, 'TotalTime':'0'}
tries = 0
candy = {}
#extract data
def fetchHex(xurl, xrequest_timeout):
try:
candyhex = requests.get(xurl, timeout=xrequest_timeout).text
return candyhex
except:
return None
# convert data to readable text
def convText():
hexText = fetchHex(url, request_timeout)
if hexText == None:
return None
bytes_object = bytes.fromhex(hexText)
coded = bytes_object.decode("ASCII")
return coded
#decode data
def decode(xkey):
xored = str()
codedText = convText()
if codedText == None:
return None
repeated_key = (xkey)*((len(codedText) // len(xkey)) + 1)
for x in range(len(codedText)):
xored += chr(ord(codedText[x]) ^ ord(repeated_key[x]))
return xored
# strip and print data
while tries < retries:
decoded = decode(key)
if decoded != None:
decodedDict = json.loads(decoded)
candyData = decodedDict.get('statusLavatrice')
for k, v in candyData.items():
if k[0:3] != "Opt" and k[0:3] != "Rec" and k[0:3] != "Ste" and k[0:3] != "SLe" and k[0:3] != "Che" and k[0:3] != "PrC" and k[0:3] != "Lan" and k[0:3] != "Fil" and k[0:3] != "Det" and k[0:3] != "Sof" and k[0:3] != "DPr" and k[0:3] != "SPr" and k[0:3] != "Wat" and k[0:3] != "rED":
if k == 'DelVal' and candyData[k] == '255':
candy['DelVal'] = '0'
else:
candy[k] = candyData[k]
TotalTime = int(candy['DelVal']) * 60 + int(candy['RemTime'])
candy['TotalTime'] = str(TotalTime)
candyJson = json.dumps(candy, indent = 4)
break
tries += 1
time.sleep(retry_delay)
if tries == retries:
candyJson = json.dumps(candyOff, indent = 4)
print(candyJson)
Full configuration.yaml here:
command_line:
- sensor:
name: 'Candy Washer Dryer'
scan_interval: 60
command_timeout: 30
command: python3 ./pyscript/candy.py
value_template: '{{ value_json }}'
json_attributes:
- WiFiStatus
- Err
- MachMd
- PrNm
- PrPh
- Temp
- SpinSp
- RemTime
- DryT
- DelVal
- TotalTime
# Configuration for Candy sensors (Washer/Dryer)
template:
#####################
## Other Templates ##
#####################
# Washer Dryer
- sensor:
- name: 'Washer/Dryer - WiFi'
icon: mdi:washing-machine
state: >
{% set WiFi = state_attr('sensor.candy_washer_dryer', 'WiFiStatus') | default(1) %}
{% if WiFi == '0' %}On
{% elif WiFi == '1' %}Off
{% else %}{{ WiFi }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Error'
icon: mdi:alert-circle
state: >
{% set Error = state_attr('sensor.candy_washer_dryer', 'Err') | default(0) %}
{% if Error == '0' %}No Errors
{% elif Error is number %}{{ Error }}
{% else %}Off
{% endif %}
- sensor:
- name: 'Washer/Dryer - Status'
icon: mdi:chart-pie
state: >
{% set Status = state_attr('sensor.candy_washer_dryer', 'MachMd') %}
{% if Status == "1" %}Not Started
{% elif Status == "2" %}Running
{% elif Status == "3" %}Paused
{% elif Status == "4" %}Setting Up
{% elif Status == "5" %}Delayed
{% elif Status == "7" %}Finished
{% elif Status == None %}Off
{% else %}{{ Status }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Program'
icon: mdi:progress-star
state: >
{% set Prog = state_attr('sensor.candy_washer_dryer', 'PrNm') %}
{% if Prog == "2" %}Cotton
{% elif Prog == "3" %}Eco 40-60
{% elif Prog == "4" %}Cotton & Prewash
{% elif Prog == "9" %}Wool & Soft Care
{% elif Prog == "10" %}Rapid 14'/30'/44'
{% elif Prog == "11" %}All in One 59'
{% elif Prog == "12" %}Extra Care
{% elif Prog == "13" %}Dry (Wool)
{% elif Prog == "14" %}Dry (Low Heat)
{% elif Prog == "15" %}Dry (High Heat)
{% elif Prog == None %}Off
{% else %}{{ Prog }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Phase'
icon: mdi:progress-question
state: >
{% set Phase = state_attr('sensor.candy_washer_dryer', 'PrPh') %}
{% if Phase == "0" %}Delayed
{% elif Phase == "1" %}Prewash
{% elif Phase == "2" %}Wash
{% elif Phase == "3" %}Rinse
{% elif Phase == "4" %}Spin/ Drain
{% elif Phase == "5" %}End
{% elif Phase == "6" %}Drying
{% elif Phase == "7" %}Error
{% elif Phase == "8" %}Steam
{% elif Phase == "9" %}Goodnight
{% elif Phase == "10" %}Spin
{% elif Phase == None %}Off
{% else %}{{ Phase }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Wash Temp'
icon: mdi:thermometer-lines
unit_of_measurement: '°C'
state: >
{% set Temp = state_attr('sensor.candy_washer_dryer', 'Temp') %}
{% if Temp == None %}0
{% elif Temp == "0" %}0
{% else %}{{ Temp }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Spin Speed'
icon: mdi:speedometer
unit_of_measurement: rpm
state: >
{% set Spin = state_attr('sensor.candy_washer_dryer', 'SpinSp') %}
{% if Spin == None %}0
{% elif Spin == "0" %}0
{% else %}{{ (Spin | int) * 100 }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Wash Time'
icon: mdi:timer
unit_of_measurement: 'mins'
state: >
{% set WashT = state_attr('sensor.candy_washer_dryer', 'RemTime') %}
{% if WashT == None %}0
{% else %}{{ (WashT | int) / 60 | round(2) }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Dry Time'
icon: mdi:clock-time-four
state: >
{% set DryT = state_attr('sensor.candy_washer_dryer', 'DryT') %}
{% if DryT == None %}Off
{% elif DryT == "0" %}Dry Off
{% elif DryT == "3" %}Iron Dry
{% elif DryT == "4" %}Dry Finished
{% elif DryT == "5" %}Dry Ending
{% else %}{{ DryT }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Delay Time'
icon: mdi:timer
unit_of_measurement: 'hours'
state: >
{% set DelT = state_attr('sensor.candy_washer_dryer', 'DelVal') %}
{% if DelT == None %}0
{% else %}{{ DelT | int }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Total Time'
icon: mdi:timer
state: >
{% set TotalT = state_attr('sensor.candy_washer_dryer', 'TotalTime') %}
{% if TotalT == None %}0
{% else %}{{ (TotalT | int) / 60 | round(2) }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Max Delay'
icon: mdi:timer
state: >
{% set Status = states('sensor.washer_dryer_wifi') %}
{% set Delay = states('sensor.washer_dryer_delay_time') | int(default=0) * 60 %}
{% set DelayMax = states('sensor.washer_dryer_max_delay') | int(default=0) %}
{% set Total = states('sensor.washer_dryer_total_time') | int %}
{% if Total == 0 %}0
{% elif Status == 'Off' %}{{ DelayMax }}
{% elif DelayMax >= Delay %}{{ DelayMax }}
{% else %}{{ Delay }}
{% endif %}
- sensor:
- name: 'Washer/Dryer - Max Time'
icon: mdi:timer
state: >
{% set Status = states('sensor.washer_dryer_wifi') %}
{% set Total = states('sensor.washer_dryer_total_time') | int(default=0) %}
{% set TMax = states('sensor.washer_dryer_max_time') | int(default=0) %}
{% set DelayMax = states('sensor.washer_dryer_delay_max') | int(default=0) %}
{% if Total == 0 %}0
{% elif Status == 'Off' %}{{ TMax }}
{% elif Total <= (TMax - DelayMax) %}{{ TMax }}
{% else %}{{ Total }}
{% endif %}
Excellent. Glad you got it working. I shall add those changes today.
Not much of an issue but the only feature not supported in the PrNm list is the WiFi setting. It doesn't appear to hold a unique value. I have not yet tested what the program entity will output when WiFi is selected and a wash is started - I would assume the machine will output the actual program selected.
I did start playing about with this but found I the Pr/PrNm was whatever was set through the app. I am not sure what key reports the WiFi control state.
Rapid 14'/30'/44' do not each hold a unique program so are bundled together.
My machine doesnt have multiple options on a setting so unable to test this myself. Looking at your full JSON response the key for this could be "PrCode": "122"
. If you remove this: and k[0:3] != "PrC"
from exclude list on line 50 of candy.py and add -
PrCode to the json_attribute or your command_line sensor. Assuming you are still under 255 characters you should be able to see if this keys value corresponds to your rapid setting.
Add commands?
I did start working on another python script to handle commands but kept running into problems. I have yet to find any official documentation on the API that would make this a lot easier. Sniffing packets got me some information but duplicating the signal in console failed. Ill take a look at the HACS candy integration see if they have solved this, last I looked they were having they same trouble. Its not a huge priority for me but I can see use cases with blackouts/ solar/ battery/ electric cost changes.
Ill open issues for all your requests and see what can be done.
I've compiled a list of the programs on my machine, for reference they are; PrNm = 15 = High Heat (dry setting) 14 = Low Heat (dry setting) 13 = Dry Wool (dry setting) 12 = Extra Care 11 = All in One 59' 10 = Rapid 14'/30'/44' 9 = Wool & Soft Care 8 = Rinse 7 = Drain & Spin 6 = Synthetic & Colours 5 = Eco 20C 4 = Cottons & Prewash 3 = Eco 40-60 2 = Cottons Wifi Setting = No specific PrNm as far as I can tell
I've added them into the configuration.yaml as follows:
Both before and after making the above changes to the config file, the program entity remains in the 'Off' state at all times. I also added 'Nm' to the state attribute on line 5 of the above code, that didn't change anything. In candy.py 'Pr' and 'PrNm' are set as equals, as far as I understand at least.
Can I get some more information which could shine light on why the program numbers are not being used correctly?