master
mpex 2016-04-12 20:32:53 +02:00
rodzic a5e904fdd5
commit fac2229397
2 zmienionych plików z 191 dodań i 1 usunięć

Wyświetl plik

@ -1,2 +1,30 @@
# EQ3-Thermostat
Python script to control EQ3 Bluetooth Thermostat
Library to control EQ3 BTLE Thermostats.
It makes use of *gatttool* which is part of the *bluez* package.
With the lib can do:
* Read current temperature and lockstate from thermostat (if changed manually)
* Activate Boostmode: 300sec fully open valve
* Deactive Boostmode: Interrupt Boostmode earlier than 300sec
* Lock Thermostat: Disable manual mode
* Unlock Thermostat: Enable manual mode
* Switch between automatic, manual and eco mode (automatic schedule on thermostat side)
* Set Temperature Offset: Set an offset to measured temperature
* Set Day/Night Mode: Change between two preset values
* Change Window Open settings: Change temperature and duration
* Set Temperature: Self explanatory (given in celcius)
* Set Time: Set date/time on the thermostat
What the lib cannot do:
* Check if device is really present. Right now no error handling is done,
as it is hard to determine wether the command was successful or not.
* Manipulation of the inbuilt programs. Not necessary for my needs
* The vacation function is not implemented

162
eq3_control.py 100644
Wyświetl plik

@ -0,0 +1,162 @@
#!/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)