Skip to content

Commit

Permalink
Attempt to fix absolute time entity program time estimate
Browse files Browse the repository at this point in the history
  • Loading branch information
ekutner committed Feb 23, 2024
1 parent 21b21de commit f011b3e
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 23 deletions.
2 changes: 1 addition & 1 deletion custom_components/home_connect_alt/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"requirements": ["home-connect-async==0.8.0"],
"loggers": ["home_connect_alt", "home_connect_async"],
"ssdp": [],
"version": "1.1.1",
"version": "1.1.2",
"zeroconf": []
}
10 changes: 6 additions & 4 deletions custom_components/home_connect_alt/select.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" Implement the Select entities of this implementation """
from __future__ import annotations
import logging
from custom_components.home_connect_alt.time import DelayedOperationTime
from home_connect_async import Appliance, HomeConnect, HomeConnectError, Events, ConditionalLogger as CL
from homeassistant.components.select import SelectEntity
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -32,13 +33,14 @@ def add_appliance(appliance:Appliance) -> None:
for program in appliance.available_programs.values():
if program.options:
for option in program.options.values():
if conf.get_entity_setting(option.key, "type") == "DelayedOperation" and entry_conf[CONF_DELAYED_OPS] == CONF_DELAYED_OPS_DEFAULT:
if conf.get_entity_setting(option.key, "type") == "DelayedOperation" and (
entry_conf[CONF_DELAYED_OPS] == CONF_DELAYED_OPS_DEFAULT or not DelayedOperationTime.has_program_run_time(appliance)):
device = DelayedOperationSelect(appliance, option.key, conf, option)
# remove the TIME delayed operation entity if it exists
reg = async_get(hass)
select_entity = reg.async_get_entity_id("time", DOMAIN, device.unique_id)
if select_entity:
reg.async_remove(select_entity)
time_entity = reg.async_get_entity_id("time", DOMAIN, device.unique_id)
if time_entity:
reg.async_remove(time_entity)

entity_manager.add(device)
elif option.allowedvalues and len(option.allowedvalues)>1:
Expand Down
47 changes: 29 additions & 18 deletions custom_components/home_connect_alt/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def add_appliance(appliance:Appliance) -> None:
for program in appliance.available_programs.values():
if program.options:
for option in program.options.values():
if conf.get_entity_setting(option.key, "type") == "DelayedOperation" and entry_conf[CONF_DELAYED_OPS]==CONF_DELAYED_OPS_ABSOLUTE_TIME:
if conf.get_entity_setting(option.key, "type") == "DelayedOperation" \
and entry_conf[CONF_DELAYED_OPS]==CONF_DELAYED_OPS_ABSOLUTE_TIME \
and DelayedOperationTime.has_program_run_time(appliance):
device = DelayedOperationTime(appliance, option.key, conf, option)
# remove the SELECT delayed operation entity if it exists
reg = async_get(hass)
Expand All @@ -54,8 +56,7 @@ class DelayedOperationTime(InteractiveEntityBase, TimeEntity):

def __init__(self, appliance: Appliance, key: str = None, conf: dict = None, hc_obj = None) -> None:
super().__init__(appliance, key, conf, hc_obj)
self._current:time = self.init_time()

self._current:time = None
@property
def name_ext(self) -> str|None:
return self._hc_obj.name if self._hc_obj.name else "Delayed operation"
Expand All @@ -69,7 +70,7 @@ def icon(self) -> str:
def available(self) -> bool:

# We must have the program run time for this entity to work
available = super().program_option_available and self.get_program_run_time() is not None
available = super().program_option_available and self.get_program_run_time(self._appliance) is not None

if not available:
self._appliance.clear_startonly_option(self._key)
Expand All @@ -84,13 +85,17 @@ async def async_set_value(self, value: time) -> None:
@property
def native_value(self) -> time:
"""Return the entity value to represent the entity state."""
if self._current is None:
self._current = self.init_time()

if self._appliance.startonly_options and self._key in self._appliance.startonly_options:
self._current = self.adjust_time(self._current, True)
else:
self._current = self.adjust_time(self._current, False)
return self._current

def adjust_time(self, t:time, set_option:bool) -> time:

def adjust_time(self, t:time, set_option:bool) -> time|None:

now = datetime.datetime.now()
endtime = datetime.datetime(year=now.year, month=now.month, day=now.day, hour=t.hour, minute=t.minute)
Expand All @@ -99,7 +104,10 @@ def adjust_time(self, t:time, set_option:bool) -> time:
# if the specified time is smaller than now then it means tomorrow
endtime += datetime.timedelta(days=1)

program_run_time = self.get_program_run_time()
program_run_time = self.get_program_run_time(self._appliance)

if not program_run_time:
return None

if endtime < now + timedelta(seconds=program_run_time):
# the set end time is closer then the program run time so change it to the expected end of the program
Expand Down Expand Up @@ -129,23 +137,26 @@ def init_time(self) -> time:
t = time(hour=inittime.hour, minute=inittime.minute)
return self.adjust_time(t, False)

def get_program_run_time(self) -> int|None:

# There seems to be a bug in HC which returns the remaining time for the previous program when there isn't an dactive one
prog_time_option = self._appliance.get_applied_program_option("BSH.Common.Option.RemainingProgramTime")
if prog_time_option and self._appliance.active_program:
return prog_time_option.value
@classmethod
def get_program_run_time(cls, appliance:Appliance) -> int|None:

prog_time_option = self._appliance.get_applied_program_option("BSH.Common.Option.FinishInRelative")
if prog_time_option:
return prog_time_option.value
time_option_keys = [
"BSH.Common.Option.RemainingProgramTime",
"BSH.Common.Option.FinishInRelative",
"BSH.Common.Option.EstimatedTotalProgramTime",
]

prog_time_option = self._appliance.get_applied_program_option("BSH.Common.Option.EstimatedTotalProgramTime")
if prog_time_option:
return prog_time_option.value
for key in time_option_keys:
o = appliance.get_applied_program_option(key)
if o:
return o

return None

@classmethod
def has_program_run_time(cls, appliance:Appliance) ->bool:
return cls.get_program_run_time(appliance) is not None


async def async_on_update(self, appliance:Appliance, key:str, value) -> None:
# reset the end time clock when a different program is selected
Expand Down

0 comments on commit f011b3e

Please sign in to comment.