Added a smart condition to the boiler

This commit is contained in:
Pierre committed 2026-10-11 10:13:37 +02:00
1 parent 6447d754ca
commit 79dd13f31d
4 files changed
+215 -6

No files matched your search

+40 -5
View File
@@ -5,6 +5,7 @@ from pathlib import Path
import time
import appdaemon.plugins.hass.hassapi as hass
import ad_toolbox.smartcondition as SmartCondition
from boilerregulator import BoilerRegulator, finite, setmode
from logger_interface import LoggerInterface
@@ -19,6 +20,7 @@ class BoilerClimate(hass.Hass, LoggerInterface):
self.boiler = self.args.get('boiler_topic', 'themaplus-condens').rstrip('/')
self.connection_topic = self.args.get('mqtt_connection_topic', self.base + '/connection')
self.uid = self.args.get('unique_id', 'appdaemon_radiator_heating')
self.entity_id = self.args.get('entity', 'climate.radiator_heating')
self.title = self.args.get('display_name', 'Chauffage radiateurs')
self.indoor = self.args['indoor_sensor']
outdoors = self.args.get('outdoor_sensors')
@@ -54,10 +56,40 @@ class BoilerClimate(hass.Hass, LoggerInterface):
for entity in self.outdoors:
self.listen_state(self._sensor_changed, entity, attribute='all')
self._discovery()
self._initialize_smart_conditions()
self.run_every(self._tick, 'now', self.interval)
self._control()
self.log_info(f'Boiler climate ready: MQTT writes={self.send_commands}, room target={self.target} °C')
def _initialize_smart_conditions(self, kwargs=None):
if getattr(self, 'smart_conditions_evaluator', None) is not None:
return
self.smart_conditions_evaluator = None
if 'smart_conditions' in self.args:
if not self.entity_exists(self.entity_id) or self.get_state(self.entity_id, attribute='temperature') is None:
self.mode = 'off'
self.run_in(self._initialize_smart_conditions, 1)
return
constants = {**self.args.get('constants', {}), 'self': self.entity_id}
self.smart_conditions_evaluator = SmartCondition.Evaluator(
self, self.args['smart_conditions'], condition_name='smart_conditions',
on_change_cb=self.on_smart_conditions_change,
constants=constants)
def on_smart_conditions_change(self, previous, result):
if result == SmartCondition.Result.Disabled:
return
mode = 'heat' if result == SmartCondition.Result.Succeeded else 'off'
if mode != self.mode:
self._set_mode(mode, 'SmartCondition')
def _set_mode(self, mode, source):
self.log_info(f'Climate mode: {self.mode} -> {mode} ({source})')
self.mode = mode
self.controller.reset()
self._control(force=True)
self._save_safely()
def _publish(self, topic, payload, retain=False):
if not isinstance(payload, str):
payload = json.dumps(payload, allow_nan=False, ensure_ascii=False)
@@ -71,7 +103,7 @@ class BoilerClimate(hass.Hass, LoggerInterface):
{'topic': self.connection_topic}],
availability_mode='all')
config = dict(common, name=None, unique_id=self.uid,
default_entity_id='climate.radiator_heating',
default_entity_id=self.entity_id,
modes=['off', 'heat'], temperature_unit='C', precision=0.1,
temp_step=0.5, min_temp=self.min_target, max_temp=self.max_target,
mode_command_topic=self.base + '/command/mode',
@@ -170,10 +202,7 @@ class BoilerClimate(hass.Hass, LoggerInterface):
if payload not in ('off', 'heat'):
self.log_warning('Rejected invalid climate mode')
return
self.mode = payload
self.controller.reset()
self._control(force=True)
self._save_safely()
self._set_mode(payload, 'MQTT')
elif topic == self.base + '/command/temperature':
try:
target = finite(payload)
@@ -286,11 +315,17 @@ class BoilerClimate(hass.Hass, LoggerInterface):
# on boiler command expiration. No retained actuator command, ever.
if not hasattr(self, 'base'):
return
evaluator = getattr(self, 'smart_conditions_evaluator', None)
try:
if self.send_commands:
self._publish(self.boiler + '/bai/SetMode/set', setmode(0, False))
except Exception as exc:
self.log_error(f'Could not publish shutdown; boiler expiration remains the fallback: {exc}')
if evaluator is not None:
try:
evaluator.deactivate()
except Exception as exc:
self.log_warning(f'Could not deactivate SmartCondition: {exc}')
try:
self._publish(self.base + '/availability', 'offline', True)
except Exception as exc:
+9
View File
@@ -221,6 +221,7 @@ guestroom_heatpump:
radiator_heating:
module: boilerclimate
class: BoilerClimate
entity: climate.radiator_heating
display_name: Chauffage radiateurs
unique_id: appdaemon_radiator_heating
indoor_sensor: sensor.corridor_temperature
@@ -230,6 +231,14 @@ radiator_heating:
target_temperature: 18
initial_mode: heat
smart_conditions:
local_expressions:
outdoor_temperature: "sensor.ext_front_temperature < sensor.ext_back_temperature ? sensor.ext_front_temperature : sensor.ext_back_temperature"
trigger_conditions: binary_sensor.someone_home and outdoor_temperature < self.temperature
blocking_conditions:
- binary_sensor.group_windows
- binary_sensor.kitchen_french_door or binary_sensor.living_room_french_door or binary_sensor.bedroom_french_door
# Radiator heating enabled with the room-thermostat bridge CLOSED.
# false keeps the climate/diagnostics but suppresses SetMode writes.
send_commands: true