Created
August 9, 2017 22:49
-
-
Save matt2005/e2c853251808b9a3bc7c98fa25cc59ca to your computer and use it in GitHub Desktop.
HeatingLogic based on https://blog.frei.media/posts/atmospheric-conditions-based-heating-control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test_sensors: | |
module: testsensors | |
class: TestSensors | |
heating_test: | |
module: heatinglogic | |
class: HeatingLogic | |
sensor_temperature_flow_actual: sensor.heating_flow_temp | |
sensor_temperature_outside_actual: sensor.heating_outside_temp | |
input_temperature_inside_setpoint: input_slider.heating_setpoint_temp | |
switch_heating: switch.heating | |
adjustment: 3 | |
gain: 1.2 | |
temperature_flow_max: 70 # C | |
temperature_flow_min: 25 # C | |
temperature_flow_tolerance_above: 5 # C | |
temperature_flow_tolerance_below: 5 # C | |
switch_heating_max_on: 150 # Minutes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import appdaemon.appapi as appapi | |
class HeatingLogic(appapi.AppDaemon): | |
def initialize(self): | |
self.log("Starting HeatingLogic ...") | |
self.gain = float(self.args['gain']) | |
self.adjustment = float(self.args['adjustment']) | |
self.temperature_flow_min = float(self.args['temperature_flow_min']) | |
self.temperature_flow_max = float(self.args['temperature_flow_max']) | |
self.temperature_flow_tolerance_above = float(self.args['temperature_flow_tolerance_above']) | |
self.temperature_flow_tolerance_below = float(self.args['temperature_flow_tolerance_below']) | |
self.switch_heating = self.args['switch_heating'] | |
self.switch_heating_max_on = float(self.args['switch_heating_max_on']) # Minutes | |
# Get current temperature information | |
self.get_sensor_temperatures() | |
# Register for sensor status updates | |
self.listen_state(self.temperature_change, entity=self.args['sensor_temperature_outside_actual']) | |
self.listen_state(self.temperature_change, entity=self.args['sensor_temperature_flow_actual']) | |
self.listen_state(self.temperature_change, entity=self.args['input_temperature_inside_setpoint']) | |
# Set inital power state | |
self.action_heating() | |
def get_sensor_temperatures(self, update_entity="", update_value=0.0): | |
# Get values from HA | |
if not update_entity: | |
self.temperature_outside_actual = float(self.get_state(self.args['sensor_temperature_outside_actual'])) | |
self.temperature_flow_actual = float(self.get_state(self.args['sensor_temperature_flow_actual'])) | |
self.temperature_inside_setpoint = float(self.get_state(self.args['input_temperature_inside_setpoint'])) | |
elif update_entity == self.args['sensor_temperature_outside_actual']: | |
self.temperature_outside_actual = update_value | |
elif update_entity == self.args['sensor_temperature_flow_actual']: | |
self.temperature_flow_actual = update_value | |
elif update_entity == self.args['input_temperature_inside_setpoint']: | |
self.temperature_inside_setpoint = update_value | |
def calculate_temperature_flow_setpoint(self): | |
""" | |
Calculate best vorlauftemperatur | |
""" | |
temperature_flow_setpoint = float(min(max(0.55*self.gain*(self.temperature_inside_setpoint**(self.temperature_outside_actual/(320-self.temperature_outside_actual*4)))*((-self.temperature_outside_actual+20)*2)+self.temperature_inside_setpoint+self.adjustment, self.temperature_flow_min), self.temperature_flow_max)) | |
return temperature_flow_setpoint | |
def calculate_heating_action(self): | |
""" | |
Decide wether to turn heating on, off or to leave it in its current state | |
""" | |
temperature_flow_setpoint = self.calculate_temperature_flow_setpoint() | |
self.log("T-flow-setpoint: "+str(temperature_flow_setpoint) +" => ") | |
if self.temperature_flow_actual < (temperature_flow_setpoint-self.temperature_flow_tolerance_below): | |
return "on" | |
elif self.temperature_flow_actual >= (temperature_flow_setpoint+self.temperature_flow_tolerance_above): | |
return "off" | |
else: | |
return "" | |
def temperature_change(self, entity, attribute, old, new, kwargs): | |
""" | |
Callback function | |
called if any temperature changes | |
""" | |
self.get_sensor_temperatures(update_entity=entity, update_value=float(new)) | |
self.action_heating() | |
def action_heating(self): | |
""" | |
Action: Turn heating on or off | |
""" | |
# Decide to turn heating on or off | |
calculated_action = self.calculate_heating_action() | |
# Execute action | |
if calculated_action == "on": | |
self.action_heating_on({}) | |
elif calculated_action == "off": | |
self.action_heating_off({}) | |
else: | |
self.log("Leaving heating in its current state (T-flow-actual: %f; T-outside-actual: %f; T-inside-setpoint: %f)" % (self.temperature_flow_actual, self.temperature_outside_actual, self.temperature_inside_setpoint)) | |
def action_heating_on(self, kwargs): | |
self.log("Turning heating on (T-flow-actual: %f; T-outside-actual: %f; T-inside-setpoint: %f)" % (self.temperature_flow_actual, self.temperature_outside_actual, self.temperature_inside_setpoint)) | |
self.call_service("switch/turn_on", entity_id=self.switch_heating) | |
# Set timer for auto turn off to prevent damage | |
self.turn_off_timer = self.run_in(self.action_heating_off, int(60*self.switch_heating_max_on), timer=True) | |
def action_heating_off(self, kwargs): | |
if 'timer' in kwargs and kwargs['timer']==True: | |
self.log("Timeout reached for heating in state 'on'. Going to turn it off =>") | |
self.log("Turning heating off (T-flow-actual: %f; T-outside-actual: %f; T-inside-setpoint: %f)" % (self.temperature_flow_actual, self.temperature_outside_actual, self.temperature_inside_setpoint)) | |
self.call_service("switch/turn_off", entity_id=self.switch_heating) | |
# cancel timer for auto turn off | |
if hasattr(self, 'turn_off_timer'): | |
self.cancel_timer(self.turn_off_timer) | |
self.log("Canceled turn_off timer") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import appdaemon.appapi as appapi | |
import datetime | |
# Declare Class | |
class TestSensors(appapi.AppDaemon): | |
#initialize() function which will be called at startup and reload | |
def initialize(self): | |
# Call to Home Assistant to turn the porch light on | |
self.log("Setting flow temp ...") | |
self.set_state("sensor.heating_flow_temp", state = 20, attributes={"unit_of_measurement": "°C", "friendly_name": "FlowTemp"}) | |
self.log("Setting outside temp ...") | |
self.set_state("sensor.heating_outside_temp", state = 10, attributes={"unit_of_measurement": "°C", "friendly_name": "OutsideTemp"}) | |
self.log("Setting set point ...") | |
self.set_state("input_slider.heating_setpoint_temp", state = 20, attributes={"initial": 0, "min": 0, "max": 35, "step": 1, "friendly_name": "SetPoint"}) | |
self.log("Setting switch heating ...") | |
self.set_state("switch.heating", state = "off", attributes={"assumed_state": True, "friendly_name": "Switch"}) | |
self.log("Setting group ...") | |
self.set_state("group.heating", state = "off", attributes={"assumed_state": False, "friendly_name": "HeatingLogicTest", "entity_id": ["sensor.heating_flow_temp","sensor.heating_outside_temp","input_slider.heating_setpoint_temp","switch.heating"]}) | |
AppDaemon.yaml contains the additions required
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Testsensors creates the components in Home assistant