Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend tumble dryer support #22

Merged
merged 10 commits into from
Oct 13, 2021
25 changes: 20 additions & 5 deletions custom_components/candy/client/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class MachineState(Enum):
DELAYED_START_SELECTION = 4
DELAYED_START_PROGRAMMED = 5
ERROR = 6
FINISHED = 7
FINISHED1 = 7
FINISHED2 = 8

def __str__(self):
if self == MachineState.IDLE:
Expand All @@ -25,7 +26,7 @@ def __str__(self):
return "Delayed start programmed"
elif self == MachineState.ERROR:
return "Error"
elif self == MachineState.FINISHED:
elif self == MachineState.FINISHED1 or self == MachineState.FINISHED2:
return "Finished"
else:
return "%s" % self
Expand Down Expand Up @@ -73,12 +74,13 @@ def __str__(self):

class DryerProgramState(Enum):
STOPPED = 0

# TODO: values
RUNNING = 2

def __str__(self):
if self == DryerProgramState.STOPPED:
return "Stopped"
elif self == DryerProgramState.RUNNING:
return "Running"
else:
return "%s" % self

Expand Down Expand Up @@ -115,15 +117,28 @@ class TumbleDryerStatus:
program: int
remaining_minutes: int
remote_control: bool
dry_level: int
refresh: bool
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the meaning of this bool value? Is this an extra feature on top of programs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a tumble dryer features. There are more dry level (as ready to be ironing or to be put in the closet) and when the machine is running reach step by step these level. You can choose to pull out some clothes (as shirts that need to be ironed) and leave t-shirt to be so dry to fold in the closet, for example.

Refresh is a feature that you can turn on to keep machine running time-by-time, so clothes don't crease.

need_clean_filter: bool
full_water_tank: bool
drylevel_selected: int
door_close: bool

@classmethod
def from_json(cls, json):
return cls(
machine_state=MachineState(int(json["StatoTD"])), # TODO?
program_state=DryerProgramState(int(json["PrPh"])),
program=int(json["Pr"]),
remaining_minutes=int(json["RemTime"]), # TODO: minutes or seconds?
remaining_minutes=int(json["RemTime"]),
remote_control=json["StatoWiFi"] == "1",
dry_level=int(json["DryLev"]),
refresh=json["Refresh"] == "1",
need_clean_filter=json["CleanFilter"] == "1",
full_water_tank=json["WaterTankFull"] == "1",
drylevel_selected=int(json["DryingManagerLevel"]),
door_close=json["DoorState"] == "1",

)


Expand Down
3 changes: 3 additions & 0 deletions custom_components/candy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
UNIQUE_ID_WASH_CYCLE_STATUS = "{0}-wash_cycle_status"
UNIQUE_ID_WASH_REMAINING_TIME = "{0}-wash_remaining_time"
UNIQUE_ID_TUMBLE_DRYER = "{0}-tumble_dryer"
UNIQUE_ID_TUMBLE_CYCLE_STATUS = "{0}-tumble_cycle_status"
UNIQUE_ID_TUMBLE_REMAINING_TIME = "{0}-tumble_remaining_time"

UNIQUE_ID_OVEN = "{0}-oven"
UNIQUE_ID_OVEN_TEMP = "{0}-oven-temp"

Expand Down
69 changes: 68 additions & 1 deletion custom_components/candy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
])
elif type(coordinator.data) is TumbleDryerStatus:
async_add_entities([
CandyTumbleDryerSensor(coordinator, config_id)
CandyTumbleDryerSensor(coordinator, config_id),
CandyTumbleStatusSensor(coordinator, config_id),
CandyTumbleRemainingTimeSensor(coordinator, config_id)
])
elif type(coordinator.data) is OvenStatus:
async_add_entities([
Expand Down Expand Up @@ -197,11 +199,76 @@ def extra_state_attributes(self) -> Mapping[str, Any]:
"program": status.program,
"remaining_minutes": status.remaining_minutes,
"remote_control": status.remote_control,
"dry_level": status.dry_level,
"dry_level_now": status.drylevel_selected,
"refresh": status.refresh,
"need_clean_filter": status.need_clean_filter,
"watertank_full": status.full_water_tank,
"door_close": status.door_close,
}

return attributes


class CandyTumbleStatusSensor(CandyBaseSensor):

def device_name(self) -> str:
return DEVICE_NAME_TUMBLE_DRYER

def suggested_area(self) -> str:
return SUGGESTED_AREA_BATHROOM

@property
def name(self) -> str:
return "Dryer cycle status"

@property
def unique_id(self) -> str:
return UNIQUE_ID_TUMBLE_CYCLE_STATUS.format(self.config_id)

@property
def state(self) -> StateType:
status: TumbleDryerStatus = self.coordinator.data
return str(status.program_state)

@property
def icon(self) -> str:
return "mdi:tumble-dryer"


class CandyTumbleRemainingTimeSensor(CandyBaseSensor):

def device_name(self) -> str:
return DEVICE_NAME_TUMBLE_DRYER

def suggested_area(self) -> str:
return SUGGESTED_AREA_BATHROOM

@property
def name(self) -> str:
return "Dryer cycle remaining time"

@property
def unique_id(self) -> str:
return UNIQUE_ID_TUMBLE_REMAINING_TIME.format(self.config_id)

@property
def state(self) -> StateType:
status: TumbleDryerStatus = self.coordinator.data
if status.machine_state in [MachineState.RUNNING, MachineState.PAUSED]:
return status.remaining_minutes
else:
return 0

@property
def unit_of_measurement(self) -> str:
return TIME_MINUTES

@property
def icon(self) -> str:
return "mdi:progress-clock"


class CandyOvenSensor(CandyBaseSensor):

def device_name(self) -> str:
Expand Down
20 changes: 20 additions & 0 deletions custom_components/candy/translations/it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"config": {
"abort": {
"already_configured": "Il dispositivo è già configurato"
},
"error": {
"unknown": "Errore sconosciuto"
},
"step": {
"user": {
"data": {
"ip_address": "Indirizzo IP del dispositivo",
"password": "Chiave di crittografia",
"use_encryption": "Utilizza chiave di crittografia"
}
}
}
},
"title": "Candy"
}
24 changes: 24 additions & 0 deletions custom_components/candy/translations/sensor.it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"state": {
"_": {
"Idle": "Inattiva",
"Running": "In corso",
"Paused": "In pausa",
"Delayed start selection": "Partenza ritardata scelta",
"Delayed start programmed": "Partenza ritardata programmata",
"Error": "Errore",
"Finished": "Completato",
"Stopped": "Fermato",
"Pre-wash": "Pre-lavaggio",
"Wash": "In lavaggio",
"Rinse": "Sto risciacquando",
"Last rinse": "Ultimo risciacquo",
"End": "Finito",
"Drying": "Asciugo",
"Steam": "In vapore",
"Spin - Good Night": "In centrifuga - Modalità Notte",
"Spin": "In centrifuga",
"Heating": "Sto riscaldando"
}
}
}
6 changes: 6 additions & 0 deletions tests/test_sensor_tumble_dryer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ async def test_main_sensor_idle(hass: HomeAssistant, aioclient_mock: AiohttpClie
'program': 1,
'remaining_minutes': 150,
'remote_control': True,
'dry_level': 2,
'dry_level_now': 1,
'refresh': False,
'need_clean_filter': False,
'watertank_full': False,
'door_close': True,
'friendly_name': 'Tumble dryer',
'icon': 'mdi:tumble-dryer'
}
Expand Down