Implement control mode selector for smart heat pumps and enhance reset event handling

This commit is contained in:
2026-06-03 21:53:32 +02:00
parent 4571c14e91
commit 6cb86875f1
3 changed files with 159 additions and 9 deletions

View File

@@ -1,19 +1,28 @@
import ad_toolbox.smartcondition as SmartCondition
import ad_toolbox.smartvalue as SmartValue
from ad_toolbox.eventhandler import EventHandler
from ad_toolbox.smartobject import SmartObject
import time
class SmartHeatpump(SmartObject):
AUTO_CONTROL_MODE = 'auto'
FORCE_ON_CONTROL_MODE = 'on'
FORCE_OFF_CONTROL_MODE = 'off'
def on_initialize_smart_object(self):
super().on_initialize_smart_object()
self.event_handlers = []
self.control_mode_entity_id = None
self.control_mode = self.AUTO_CONTROL_MODE
if 'quick_resume' in self.args:
self.quick_resume_sensor = self.create_entity(self.args['quick_resume']['sensor'])
#is_on = 'on' if self.entity.get_state() != 'off' else 'off'
#if self.get_state(self.args['quick_resume']['sensor']) != 'on': # if the sensor doesn't exist I wan't to set it as well
# self.set_state(self.args['quick_resume']['sensor'],state = is_on)
self.listen_event(lambda event_name, data, kwargs: self.set_state(self.args['quick_resume']['sensor'],state = 'off'),self.args['quick_resume']['reset_event'])
self.register_reset_events(self.args['quick_resume'], "quick_resume")
self.target_temperature = SmartValue.Evaluator(self,self.args['target_temperature'], expression_name = 'target_temperature', on_change_cb = self.on_change_target_temperature, constants = self.constants)
self.hvac_mode = SmartValue.Evaluator(self,self.args['hvac_mode'], expression_name = 'hvac_mode', on_change_cb = self.on_change_hvac_mode, constants = self.constants)
@@ -28,12 +37,129 @@ class SmartHeatpump(SmartObject):
self.fan_mode = None
self.smart_conditions_evaluator = SmartCondition.Evaluator(self,self.args['smart_conditions'],condition_name = "smart_conditions",on_change_cb = self.on_smart_conditions_change,constants = self.constants, templates_library = self.templates_library)
if 'control_mode_selector' in self.args or 'heatpump_override' in self.args:
self.initialize_control_mode_selector()
if 'input_buttons' in self.args:
if 'turn_on' in self.args['input_buttons']:
self.listen_state(lambda entity, attribute, old, new, kwargs: self.on_turn_on_button(),self.args['input_buttons']['turn_on'])
if 'turn_off' in self.args['input_buttons']:
self.listen_state(lambda entity, attribute, old, new, kwargs: self.on_turn_off_button(),self.args['input_buttons']['turn_off'])
def initialize_control_mode_selector(self):
selector_args = self.args['control_mode_selector'] if 'control_mode_selector' in self.args else self.args['heatpump_override']
if isinstance(selector_args, str):
self.control_mode_entity_id = selector_args
elif isinstance(selector_args, dict) and 'sensor' in selector_args:
self.control_mode_entity_id = selector_args['sensor']
else:
self.log_warning("control_mode_selector must be a string entity_id or a dict with a 'sensor' key")
return
self.create_entity(
self.control_mode_entity_id,
state = self.AUTO_CONTROL_MODE,
attributes = {
'options': [self.AUTO_CONTROL_MODE, self.FORCE_ON_CONTROL_MODE, self.FORCE_OFF_CONTROL_MODE]
},
icon = 'mdi:power-settings'
)
current_mode = self.get_state(self.control_mode_entity_id)
self.control_mode = self.normalize_control_mode(current_mode)
self.listen_state(self.on_control_mode_change, self.control_mode_entity_id)
self.listen_event(
self.on_control_mode_select_option_service,
'call_service',
domain='input_select',
service='select_option',
entity_id=self.control_mode_entity_id
)
self.register_reset_events(selector_args, "control_mode_selector")
self.apply_control_mode(self.control_mode)
def on_control_mode_select_option_service(self, event_name, data, kwargs):
service_data = data.get('service_data', {}) if isinstance(data, dict) else {}
service_entity_id = service_data.get('entity_id')
if isinstance(service_entity_id, list):
if self.control_mode_entity_id not in service_entity_id:
return
elif isinstance(service_entity_id, str):
if service_entity_id != self.control_mode_entity_id:
return
else:
return
mode = service_data.get('option')
if mode == None or mode == self.control_mode:
return
self.set_state(self.control_mode_entity_id, state = mode)
self.on_control_mode_change(self.control_mode_entity_id, 'state', self.control_mode, mode, {})
def register_reset_events(self, config, config_name):
if not isinstance(config, dict):
return
reset_events = config.get('reset_events', config.get('reset_event'))
if reset_events is None:
return
if isinstance(reset_events, str):
reset_events = [reset_events]
elif not isinstance(reset_events, list):
self.log_warning(f"{config_name}.reset_events must be a string or a list of events")
return
self.event_handlers.append(EventHandler(self, reset_events, self.on_reset_event, config_name))
def on_reset_event(self, event_name, event_data, config_name):
if config_name == "quick_resume":
if 'quick_resume' in self.args:
self.set_state(self.args['quick_resume']['sensor'], state='off')
elif config_name == "control_mode_selector":
if self.control_mode_entity_id:
self.set_state(self.control_mode_entity_id, state=self.AUTO_CONTROL_MODE)
def normalize_control_mode(self, mode):
if mode in [self.AUTO_CONTROL_MODE, self.FORCE_ON_CONTROL_MODE, self.FORCE_OFF_CONTROL_MODE]:
return mode
return self.AUTO_CONTROL_MODE
def on_control_mode_change(self, entity, attribute, old, new, kwargs):
if old == new:
return
previous_mode = self.control_mode
self.control_mode = self.normalize_control_mode(new)
if self.control_mode != new:
self.log_warning(f"Invalid control mode '{new}' on {entity}, falling back to '{self.control_mode}'")
self.set_state(entity, state = self.control_mode)
self.apply_control_mode(self.control_mode, previous_mode)
def apply_control_mode(self, mode, previous_mode = None):
if mode == self.AUTO_CONTROL_MODE:
self.log_info("Heatpump control mode is auto, SmartCondition is enabled")
self.smart_conditions_evaluator.activate()
return
self.smart_conditions_evaluator.deactivate()
if mode == self.FORCE_ON_CONTROL_MODE:
self.log_info("Heatpump control mode is on, forcing Heatpump on")
self.start_cooling()
elif mode == self.FORCE_OFF_CONTROL_MODE:
self.log_info("Heatpump control mode is off, forcing Heatpump off")
self.stop_cooling()
if 'quick_resume' in self.args:
self.set_state(self.args['quick_resume']['sensor'],state = 'off')
def on_turn_on_button(self): self.start_cooling()
def on_turn_off_button(self):
@@ -70,6 +196,10 @@ class SmartHeatpump(SmartObject):
def on_smart_conditions_change(self,prev_result,result):
if self.control_mode != self.AUTO_CONTROL_MODE:
self.log_info(f"SmartCondition update ignored because control mode is '{self.control_mode}'")
return
if result == SmartCondition.Result.Succeeded:
self.start_cooling()
else: