-
Notifications
You must be signed in to change notification settings - Fork 0
/
thermostat.py
140 lines (109 loc) · 4.52 KB
/
thermostat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import RPi.GPIO as GPIO
import logging
from scheduler import Schedule
import json
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
broker = 'homeassistant.local'
topic = 'home-assistant/thermostat'
lo_topic = f'{topic}/setpoint_low'
hi_topic = f'{topic}/setpoint_high'
hysteresis_topic = f'{topic}/hysteresis'
temp_topic = f'{topic}/temperature'
status_topic = f'{topic}/status'
temp_select_topic = f'{topic}/temp_select'
temp_control_topic = f'{topic}/temp_control'
fan_topic = f'{topic}/fan'
MQTT_AUTH = json.load(open('mqtt_credentials.json', 'r'))
class Thermostat:
def __init__(self, temp_select_pin, temp_control_pin, fan_pin):
self.temp_select_pin = temp_select_pin
self.temp_control_pin = temp_control_pin
self.fan_pin = fan_pin
GPIO.setup(temp_select_pin, GPIO.OUT)
GPIO.setup(temp_control_pin, GPIO.OUT)
GPIO.setup(fan_pin, GPIO.OUT)
self.heating = False
self.cooling = False
self.fan_is_on = False
self.schedule = Schedule()
self.log = logging.getLogger("Thermostat")
self.log.setLevel(logging.DEBUG)
self.mqtt_client = mqtt.Client()
self.mqtt_client.username_pw_set(**MQTT_AUTH)
self.mqtt_client.connect(broker)
self.mqtt_client.loop_start()
def fan_on(self):
GPIO.output(self.fan_pin, True)
self.fan_is_on = True
self.mqtt_client.publish(fan_topic, True)
def fan_off(self):
GPIO.output(self.fan_pin, False)
self.fan_is_on = False
self.mqtt_client.publish(fan_topic, False)
def heat_on(self):
GPIO.output(self.temp_select_pin, False)
GPIO.output(self.temp_control_pin, True)
self.log.info(f"Sending MQTT message to {status_topic}")
self.mqtt_client.publish(status_topic, 'Heating')
self.mqtt_client.publish(temp_select_topic, False)
self.mqtt_client.publish(temp_select_topic, True)
self.fan_on()
self.heating = True
self.cooling = False
def ac_on(self):
GPIO.output(self.temp_select_pin, True)
GPIO.output(self.temp_control_pin, True)
self.log.info(f"Sending MQTT message to {status_topic}")
self.mqtt_client.publish(status_topic, 'Cooling')
self.mqtt_client.publish(temp_select_topic, True)
self.mqtt_client.publish(temp_control_topic, True)
self.fan_on()
self.heating = False
self.cooling = True
def all_off(self, reset_hysteresis=True):
GPIO.output(self.temp_control_pin, False)
GPIO.output(self.temp_select_pin, False)
self.fan_off()
self.mqtt_client.publish(status_topic, 'Off')
self.mqtt_client.publish(temp_select_topic, False)
self.mqtt_client.publish(temp_control_topic, False)
if reset_hysteresis:
self.heating = False
self.cooling = False
def _set_target_temp_range(self, low_temp, high_temp, hysteresis=1):
# This is called via update_state, and shouldn't be called directly.
assert low_temp < high_temp, "Invalid temp range -- provide as [low, high]"
assert low_temp < (
high_temp - hysteresis
), "Temp range too small for hysteresis"
self.mqtt_client.publish(lo_topic, low_temp)
self.mqtt_client.publish(hi_topic, high_temp)
self.mqtt_client.publish(hysteresis_topic, hysteresis)
self.low_temp = low_temp
self.high_temp = high_temp
self.hysteresis = hysteresis
def update_state(self, current_temp):
self._set_target_temp_range(*self.schedule.get_current_target_temp_range())
self.mqtt_client.publish(temp_topic, f"{current_temp:.1f}")
self.log.info(
"Thermostat state updating -- {%.2f} ({%.2f}) | [%.2f - %.2f]"
% (current_temp, self.local_temp, self.low_temp, self.high_temp)
)
if current_temp < self.low_temp:
self.log.critical("Heat, on")
self.heat_on()
elif current_temp > self.high_temp:
self.log.critical("AC, on")
self.ac_on()
elif self.heating and current_temp > self.low_temp + self.hysteresis:
self.log.critical("Off, warm")
self.all_off()
elif self.cooling and current_temp < self.high_temp - self.hysteresis:
self.log.critical("Off, cool")
self.all_off()
# In hysteresis
else:
self.mqtt_client.publish(status_topic, 'Off')
self.log.critical("Off, maintaining")
pass