Compare commits
2 Commits
89b08b1fc5
...
6cb86875f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cb86875f1 | |||
| 4571c14e91 |
Submodule apps/ad_toolbox updated: 943361453a...56e3f6e9e0
@@ -26,15 +26,17 @@ smart_heating_templates_library:
|
|||||||
|
|
||||||
templates_library:
|
templates_library:
|
||||||
clim_conditions:
|
clim_conditions:
|
||||||
|
local_expressions:
|
||||||
|
temp_threshold: "sleeping_in_the_room ? 0.5 : 1"
|
||||||
trigger_conditions:
|
trigger_conditions:
|
||||||
- "not binary_sensor.heatpump_winter_mode and (ext_temperature >= room_temperature or ext_temperature >= target_temperature or cant_open_window) and room_temperature >= ((quick_resume or sleeping_in_the_room)? target_temperature : (target_temperature + 1))"
|
- "not binary_sensor.heatpump_winter_mode and (ext_temperature >= room_temperature or ext_temperature >= target_temperature or cant_open_window) and room_temperature >= (quick_resume ? target_temperature : (target_temperature + temp_threshold))"
|
||||||
- not binary_sensor.heatpump_winter_mode and self and (ext_temperature >= target_temperature or cant_open_window)
|
- not binary_sensor.heatpump_winter_mode and self and (ext_temperature >= target_temperature or cant_open_window)
|
||||||
- binary_sensor.heatpump_winter_mode and room_temperature <= (target_temperature - 1) and ext_temperature <= 10
|
- binary_sensor.heatpump_winter_mode and room_temperature <= (target_temperature - 1) and ext_temperature <= 10
|
||||||
- binary_sensor.heatpump_winter_mode and self and room_temperature < target_temperature
|
- binary_sensor.heatpump_winter_mode and self and room_temperature < target_temperature
|
||||||
blocking_conditions:
|
blocking_conditions:
|
||||||
- binary_sensor.is_everybody_sleeping and not sleeping_in_the_room and not stay_on_while_sleeping
|
- binary_sensor.is_everybody_sleeping and not sleeping_in_the_room and not stay_on_while_sleeping
|
||||||
- not room_occupancy
|
- not room_occupancy
|
||||||
- not binary_sensor.someone_home #and not (binary_sensor.someone_in_town and sensor.day_interval == 'Nuit')
|
- not binary_sensor.someone_home
|
||||||
- window_open
|
- window_open
|
||||||
|
|
||||||
desk_heatpump:
|
desk_heatpump:
|
||||||
@@ -69,9 +71,15 @@ desk_heatpump:
|
|||||||
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
||||||
template_conditions: <clim_conditions>
|
template_conditions: <clim_conditions>
|
||||||
|
|
||||||
|
control_mode_selector:
|
||||||
|
sensor: input_select.desk_heatpump_override
|
||||||
|
reset_events:
|
||||||
|
- good_morning
|
||||||
|
- good_bye
|
||||||
|
|
||||||
quick_resume:
|
quick_resume:
|
||||||
sensor: binary_sensor.desk_heatpump_quick_resume
|
sensor: binary_sensor.desk_heatpump_quick_resume
|
||||||
reset_event: good_morning
|
reset_events: good_morning
|
||||||
|
|
||||||
#input_buttons:
|
#input_buttons:
|
||||||
# turn_on: input_button.clim_du_bureau_turn_on
|
# turn_on: input_button.clim_du_bureau_turn_on
|
||||||
@@ -101,7 +109,7 @@ bedroom_heatpump:
|
|||||||
auto: binary_sensor.heatpump_winter_mode
|
auto: binary_sensor.heatpump_winter_mode
|
||||||
quiet: True
|
quiet: True
|
||||||
|
|
||||||
target_temperature: sensor.target_temperature_heatpump
|
target_temperature: sensor.target_temperature_heatpump + 0.5
|
||||||
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
hvac_mode: "binary_sensor.heatpump_winter_mode ? 'heat' : 'cool'"
|
||||||
fan_mode: sensor.bedroom_heatpump_fan_mode
|
fan_mode: sensor.bedroom_heatpump_fan_mode
|
||||||
|
|
||||||
@@ -111,7 +119,13 @@ bedroom_heatpump:
|
|||||||
|
|
||||||
quick_resume:
|
quick_resume:
|
||||||
sensor: binary_sensor.bedroom_heatpump_quick_resume
|
sensor: binary_sensor.bedroom_heatpump_quick_resume
|
||||||
reset_event: good_morning
|
reset_events: good_morning
|
||||||
|
|
||||||
|
control_mode_selector:
|
||||||
|
sensor: input_select.bedroom_heatpump_override
|
||||||
|
reset_events:
|
||||||
|
- good_morning
|
||||||
|
- good_bye
|
||||||
|
|
||||||
living_room_heatpump:
|
living_room_heatpump:
|
||||||
module: smartheatpump
|
module: smartheatpump
|
||||||
@@ -142,6 +156,12 @@ living_room_heatpump:
|
|||||||
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
callback_delay: 10 # on the morning it can happen that we ask to start and stop the heat pump immediately afterwards which in result in the heat pump ignoring the second command
|
||||||
template_conditions: <clim_conditions>
|
template_conditions: <clim_conditions>
|
||||||
|
|
||||||
|
control_mode_selector:
|
||||||
|
sensor: input_select.living_room_heatpump_override
|
||||||
|
reset_events:
|
||||||
|
- good_morning
|
||||||
|
- good_bye
|
||||||
|
|
||||||
quick_resume:
|
quick_resume:
|
||||||
sensor: binary_sensor.living_room_heatpump_quick_resume
|
sensor: binary_sensor.living_room_heatpump_quick_resume
|
||||||
reset_event: good_morning
|
reset_events: good_morning
|
||||||
@@ -9,6 +9,10 @@ virtual_events:
|
|||||||
event_name: good_morning
|
event_name: good_morning
|
||||||
event_condition: not binary_sensor.is_anybody_sleeping
|
event_condition: not binary_sensor.is_anybody_sleeping
|
||||||
|
|
||||||
|
good_bye:
|
||||||
|
event_name: good_bye
|
||||||
|
event_condition: not binary_sensor.someone_home
|
||||||
|
|
||||||
turn_off_all_lights:
|
turn_off_all_lights:
|
||||||
event_name: turn_off_all_lights
|
event_name: turn_off_all_lights
|
||||||
event_condition: binary_sensor.is_everybody_sleeping
|
event_condition: binary_sensor.is_everybody_sleeping
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
import ad_toolbox.smartcondition as SmartCondition
|
import ad_toolbox.smartcondition as SmartCondition
|
||||||
import ad_toolbox.smartvalue as SmartValue
|
import ad_toolbox.smartvalue as SmartValue
|
||||||
|
from ad_toolbox.eventhandler import EventHandler
|
||||||
from ad_toolbox.smartobject import SmartObject
|
from ad_toolbox.smartobject import SmartObject
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class SmartHeatpump(SmartObject):
|
class SmartHeatpump(SmartObject):
|
||||||
|
|
||||||
|
AUTO_CONTROL_MODE = 'auto'
|
||||||
|
FORCE_ON_CONTROL_MODE = 'on'
|
||||||
|
FORCE_OFF_CONTROL_MODE = 'off'
|
||||||
|
|
||||||
def on_initialize_smart_object(self):
|
def on_initialize_smart_object(self):
|
||||||
super().on_initialize_smart_object()
|
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:
|
if 'quick_resume' in self.args:
|
||||||
self.quick_resume_sensor = self.create_entity(self.args['quick_resume']['sensor'])
|
self.quick_resume_sensor = self.create_entity(self.args['quick_resume']['sensor'])
|
||||||
#is_on = 'on' if self.entity.get_state() != 'off' else 'off'
|
#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
|
#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.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.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)
|
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.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)
|
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 'input_buttons' in self.args:
|
||||||
if 'turn_on' in self.args['input_buttons']:
|
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'])
|
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']:
|
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'])
|
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_on_button(self): self.start_cooling()
|
||||||
|
|
||||||
def on_turn_off_button(self):
|
def on_turn_off_button(self):
|
||||||
@@ -70,6 +196,10 @@ class SmartHeatpump(SmartObject):
|
|||||||
|
|
||||||
|
|
||||||
def on_smart_conditions_change(self,prev_result,result):
|
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:
|
if result == SmartCondition.Result.Succeeded:
|
||||||
self.start_cooling()
|
self.start_cooling()
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user