kopia lustrzana https://github.com/mpex/EQ3-Thermostat
163 wiersze
7.2 KiB
Python
163 wiersze
7.2 KiB
Python
#!/usr/bin/python3
|
|
# -*-coding: utf-8 -*-
|
|
|
|
import datetime
|
|
import subprocess
|
|
import time
|
|
|
|
|
|
class EQ3Thermostat(object):
|
|
|
|
def __init__(self, address):
|
|
self.address = address
|
|
self.locked = False
|
|
self.temperature = 0
|
|
self.update()
|
|
|
|
def update(self):
|
|
"""Reads the current temperature from the thermostat. We need to kill
|
|
the gatttool process as the --listen option puts it into an infinite
|
|
loop."""
|
|
p = subprocess.Popen(["timeout", "-s", "INT", "2", "gatttool", "-b",
|
|
self.address, "--char-write-req", "-a", "0x0411",
|
|
"-n", "03", "--listen"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
out, err = p.communicate()
|
|
value_string = out.decode("utf-8")
|
|
|
|
if "Notification handle" in value_string:
|
|
value_string_splt = value_string.split()
|
|
temperature = value_string_splt[-1]
|
|
locked = value_string_splt[-4]
|
|
try:
|
|
subprocess.Popen.kill(p)
|
|
except ProcessLookupError:
|
|
pass
|
|
|
|
if locked == "20":
|
|
self.locked = True
|
|
elif locked == "00":
|
|
self.locked = False
|
|
else:
|
|
print("Could not read lockstate of {}".format(self.address))
|
|
|
|
try:
|
|
self.temperature = int(temperature, 16) / 2
|
|
except Exception as e:
|
|
print("Getting temperature of {} failed {}".format(self.address, e))
|
|
|
|
def activate_boostmode(self):
|
|
"""Boostmode fully opens the thermostat for 300sec."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "4501"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def deactivate_boostmode(self):
|
|
"""Use only to stop boostmode before 300sec."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "4500"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_automatic_mode(self):
|
|
"""Put thermostat in automatic mode."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "4000"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_manual_mode(self):
|
|
"""Put thermostat in manual mode."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "4040"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_eco_mode(self):
|
|
"""Put thermostat in eco mode."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "4080"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def lock_thermostat(self):
|
|
"""Locks the thermostat for manual use."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "8001"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def unlock_thermostat(self):
|
|
"""Unlocks the thermostat for manual use."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "8000"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_temperature(self, temperature):
|
|
"""Transform the temperature in celcius to make it readable to the thermostat."""
|
|
temperature = hex(int(2 * float(temperature)))[2:]
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "41{}".format(temperature)],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
# Block for 3 secs to let the thermostat adjust the temperature
|
|
time.sleep(3)
|
|
|
|
def set_temperature_offset(self, offset):
|
|
"""Untested."""
|
|
temperature = hex(int(2 * float(offset) + 7))[2:]
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "13{}".format(temperature)],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_day(self):
|
|
"""Puts thermostat into day mode (sun icon)."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "43"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_night(self):
|
|
"""Puts thermostat into night mode (moon icon)."""
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "44"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_day_night(self, night, day):
|
|
"""Sets comfort temperature for day and night."""
|
|
day = hex(int(2 * float(day)))[2:]
|
|
night = hex(int(2 * float(night)))[2:]
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "11{}{}".format(day, night)],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_windows_open(self, temperature, duration_min):
|
|
"""Untested."""
|
|
temperature = hex(int(2 * float(temperature)))[2:]
|
|
duration_min = hex(int(duration_min / 5.0))[2:]
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", "11{}{}".format(temperature, duration_min)],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
def set_time(self, datetimeobj):
|
|
"""Takes a datetimeobj (like datetime.datetime.now()) and sets the time
|
|
in the thermostat."""
|
|
command_prefix = "03"
|
|
year = "{:02X}".format(datetimeobj.year % 100)
|
|
month = "{:02X}".format(datetimeobj.month)
|
|
day = "{:02X}".format(datetimeobj.day)
|
|
hour = "{:02X}".format(datetimeobj.hour)
|
|
minute = "{:02X}".format(datetimeobj.minute)
|
|
second = "{:02X}".format(datetimeobj.second)
|
|
control_string = "{}{}{}{}{}{}{}".format(
|
|
command_prefix, year, month, day, hour, minute, second)
|
|
p = subprocess.Popen(["gatttool", "-b", self.address, "--char-write-req",
|
|
"-a", "0x0411", "-n", control_string],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
# Block for 3 secs to let the thermostat adjust the settings
|
|
time.sleep(3)
|
|
|
|
if __name__ == '__main__':
|
|
h = EQ3Thermostat("00:AA:BB:CC:DD:EE")
|
|
# Take some time
|
|
time.sleep(5)
|
|
# Deactivate autonomous behavior on the thermostat
|
|
h.set_manual_mode()
|
|
# Set current date
|
|
h.set_time(datetime.datetime.now())
|
|
# Set the current temperature
|
|
h.set_temperature(20)
|