def _setup(self): from eq3bt import Thermostat _LOGGER.info("Adding %d %s devices", len(self.devices), repr(self)) for name, obj in self.devices.items(): if isinstance(obj, str): self.devices[name] = {"mac": obj, "thermostat": Thermostat(obj)} elif isinstance(obj, dict): self.devices[name] = { "mac": obj["mac"], "thermostat": Thermostat(obj["mac"]), "discovery_temperature_topic": obj.get( "discovery_temperature_topic" ), "discovery_temperature_template": obj.get( "discovery_temperature_template" ), } else: raise TypeError("Unsupported configuration format") _LOGGER.debug( "Adding %s device '%s' (%s)", repr(self), name, self.devices[name]["mac"], )
def plugin_init(self, enableplugin=None): plugin.PluginProto.plugin_init(self, enableplugin) self.readinprogress = 0 if self.enabled: try: self.blestatus = BLEHelper.BLEStatus[ 0] # 0 is hardwired in library self.blestatus.registerdataprogress( self.taskindex) # needs continous access except: pass self.ports = str(self.taskdevicepluginconfig[0]) try: self.thermostat = Thermostat( str(self.taskdevicepluginconfig[0])) self.initialized = True time.sleep(1) except: self.initialized = False if self.interval > 2: nextr = self.interval - 2 else: nextr = 0 self._lastdataservetime = rpieTime.millis() - (nextr * 1000) else: self.ports = ""
def mode_off(self, radiator): if radiator in self.radiators.keys(): try: t = Thermostat(self.radiators[radiator]) t.mode = Mode.Closed return t.mode_readable except: return 'timeout'
def mode_manual(self, radiator, temp): if radiator in self.radiators.keys(): try: t = Thermostat(self.radiators[radiator]) t.mode = Mode.Manual t.target_temperature = temp return self.__print_temp(t.mode_readable) except: return 'timeout'
def cli(ctx, mac, debug): """ Tool to query and modify the state of EQ3 BT smart thermostat. """ if debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) thermostat = Thermostat(mac) thermostat.update() ctx.obj = thermostat if ctx.invoked_subcommand is None: ctx.invoke(state)
def plugin_init(self, enableplugin=None): plugin.PluginProto.plugin_init(self, enableplugin) self.readinprogress = 0 if self.enabled: try: self.thermostat = Thermostat( str(self.taskdevicepluginconfig[0])) self.initialized = True time.sleep(1) except: self.initialized = False if self.interval > 2: nextr = self.interval - 2 else: nextr = 0 self._lastdataservetime = rpieTime.millis() - (nextr * 1000)
def callback(topic, payload, config): log.debug("%s %s", topic, payload) room = topic.split("/")[2] mode = { b'0': Mode.Closed, b'1': Mode.Open, }.get(payload) if mode is None: log.warning("Ignoring invalid payload on %s", topic) return addresses = config['radiators'].get(room, "") if not addresses: # Control message is for a radiator we're not responsible for. log.debug("No EQ3 addresses in config for %s", room) return success = True for address in addresses.split(","): for attempt in range(10): try: Thermostat(address).mode = mode log.info("Set %s in %s to %s", address, room, mode) break except BTLEException: log.warning("Couldn't set mode %s for %s in %s", mode, address, room) sleep(1) else: success = False # Only post acknowledgment to MQTT topic if all thermostats were controlled. if success: return [("{}/ack".format(topic), payload)]
def _setup(self): from eq3bt import Thermostat for name, mac in self.devices.items(): self.devices[name] = Thermostat(mac) self._modes_mapper = self.ModesMapper()
def _setup(self): from eq3bt import Thermostat _LOGGER.info("Adding %d %s devices", len(self.devices), repr(self)) for name, mac in self.devices.items(): _LOGGER.debug("Adding %s device '%s' (%s)", repr(self), name, mac) self.devices[name] = Thermostat(mac) self._modes_mapper = self.ModesMapper()
def get_heaters(room): db = get_db() heaters = db.execute("SELECT * FROM heater WHERE room_fk = ?;", (room['id'], )).fetchall() updated_heaters = [] for heater in heaters: h = Heater(heater) h.room_name = room['name'] h.thermostat = Thermostat(heater['mac']) h.thermostat.update() updated_heaters.append(h) return updated_heaters
def air_end(): db = get_db() heaters = db.execute("SELECT * FROM heater").fetchall() response = {'message': 'success'} for heater in heaters: h = Heater(heater) h.thermostat = Thermostat(h.mac) h.thermostat.mode = 2 # Set mode to auto to return to current desired temperature h.thermostat.update() response[h.name] = { 'temperature': h.thermostat.target_temperature, 'mode': h.thermostat.mode_readable } return jsonify(response)
def air_start(): db = get_db() heaters = db.execute("SELECT * FROM heater").fetchall() response = {'message': 'success'} for heater in heaters: h = Heater(heater) h.thermostat = Thermostat(h.mac) h.thermostat.mode = 3 # set to manual mode (3) h.thermostat.target_temperature = 5 h.thermostat.update() response[h.name] = { 'temperature': h.thermostat.target_temperature, 'mode': h.thermostat.mode_readable } return jsonify(response)
def get_status(self, radiator): if radiator in self.radiators.keys(): try: t = Thermostat(self.radiators[radiator]) t.update() answer = '\n' answer += ' Locked: ' + str(t.locked) + '\n' answer += ' Battery low: ' + str(t.low_battery) + '\n' answer += ' Window open: ' + str(t.window_open) + '\n' answer += ' Window open temp: ' + self.__print_temp( t.window_open_temperature) + '\n' answer += ' Window open time: ' + str( t.window_open_time) + '\n' answer += ' Boost: ' + str(t.boost) + '\n' answer += ' Current target temp: ' + self.__print_temp( t.target_temperature) + '\n' answer += ' Current comfort temp: ' + self.__print_temp( t.comfort_temperature) + '\n' answer += ' Current eco temp: ' + self.__print_temp( t.eco_temperature) + '\n' answer += ' Valve: ' + str(t.valve_state) return answer except: return 'timeout'
from eq3bt import Thermostat import subprocess import sys import time if len(sys.argv) > 1: # CMD Parameter if sys.argv[1] == "check": #Check print("eq3OK") if sys.argv[1] == "getValue": #Get Informatioms from BTDevice thermostat = Thermostat(sys.argv[2]) thermostat.update() if str(thermostat.mode) == "Mode.Closed": thermostat.mode = 3 #Set Mode Manual thermostat.update() print(str(thermostat.target_temperature) + ";" + str(thermostat.valve_state) + ";" + str(thermostat.low_battery)) time.sleep(1) if sys.argv[1] == "setValue": #Set Temperature to BTDevice thermostat = Thermostat(sys.argv[2]) thermostat.target_temperature = float(sys.argv[3]) thermostat.update() print ("OK") else: print("Possible Arguments:"); print(" check"); print(" getValue [MAC]"); print(" setValue [MAC] [Temperature]");
import paho.mqtt.client as mqtt from datetime import datetime from eq3bt import Thermostat import logging import time import os def mqtt_on_connect(client, userdata, flags, rc): logging.info("connected with mqtt server") thermostat = { 'bad1': Thermostat('00:1A:22:08:05:20'), 'bad2': Thermostat('00:1A:22:08:05:39'), 'leon': Thermostat('00:1A:22:0d:0d:92'), 'dach': Thermostat('00:1A:22:10:81:6B'), 'wohn': Thermostat('00:1A:22:10:84:0E'), } def set_target_temperature(client, thermostat, message): logging.info("%s %s", message.topic, message.payload) name = message.topic.split('/')[1] temp = float(message.payload) try: logging.info("set target temperature for '%s' to %.1f", name, temp) thermostat[name].target_temperature = temp client.publish("thermostat/" + name + "/temperature", str(thermostat[name].target_temperature)) except Exception:
def __init__(self, MAC): self.MAC = MAC self.trv = Thermostat(MAC)
#!/usr/bin/env python3 import argparse import re import time import signal import logging import sys import schedule import paho.mqtt.client as mqtt from eq3bt import Thermostat thermostatBallpit = Thermostat('00:1A:22:0D:BE:EE') thermostatCantina = Thermostat('00:1A:22:0D:C2:5D') temperature_off = 12 temperature_on = 19 def on_connect(client, userdata, flags, rc): log.debug("Connected with result code " + str(rc)) client.subscribe("foobar/oben/baellebad/heizung/action") client.subscribe("foobar/oben/cantina/heizung/action") sendReadings() def on_publish(client, userdata, mid): log.debug("mid: " + str(mid))
btmac = sys.argv[1] device_id = sys.argv[2] if len(sys.argv) == 3: device_name = device_id else: device_name = sys.argv[3] logging.basicConfig(filename=device_id + '.log', level=logging.DEBUG, format='%(asctime)s - %(message)s', datefmt="%Y-%m-%d %H:%M:%S") logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) l = logging.getLogger(__name__) l.debug("Connecting eq3bt " + btmac + " to " + device_id) thermostat = Thermostat(btmac) import homieconnect mqtt_settings = homieconnect.mqtt_settings def to_homie_float(value): if not value: return (0) return (value) import homie.node.property.property_boolean as property_boolean #from homie.node.property.property_boolean import Property_Boolean
class myTRV: MAC = None gateTopic = None trvName = None trv = None mqttClient = None mqttGateTopic = None extTemp = 0 logger = None def __init__(self, MAC): self.MAC = MAC self.trv = Thermostat(MAC) def trvReadStateJSON(self): readSuccess = True try: self.trv.update() except: time.sleep(5) self.logger.error("RS1: Failed to communicate with " + self.trvName) try: self.trv.update() except: self.logger.error("RS2: Failed to communicate with " + self.trvName) readSuccess = False outputValues = "{\"trv\":\"" outputValues += self.MAC if readSuccess: outputValues += "\",\"temp\":\"" outputValues += str(self.trv.target_temperature) outputValues += "\",\"ctemp\":\"" outputValues += str( self.trv.target_temperature) if self.extTemp == 0 else str( self.extTemp) outputValues += "\",\"mode\":\"" outputValues += self.homeAssistantMode(self.trv.mode.value) outputValues += "\",\"boost\":\"" outputValues += "active" if self.trv.boost else "inactive" outputValues += "\",\"valve\":\"" outputValues += str(self.trv.valve_state) outputValues += "\",\"locked\":\"" outputValues += "locked" if self.trv.locked else "unlocked" outputValues += "\",\"battery\":\"" outputValues += "0" if self.trv.low_battery else "100" outputValues += "\",\"window\":\"" outputValues += "open" if self.trv.window_open else "closed" outputValues += "\",\"error\":\"\"}" else: outputValues += "\",\"error\":\"connection failed\"}" return outputValues def eq3Mode(self, homeAssistantModeIn): modeToSet = str(homeAssistantModeIn) modeToSet = modeToSet.lower() eq3ObjMode = Mode(3) if modeToSet == "auto": eq3ObjMode = Mode(2) if modeToSet == "off": eq3ObjMode = Mode(0) if modeToSet == "on": eq3ObjMode = Mode(1) if modeToSet == "boost": eq3ObjMode = Mode(5) return eq3ObjMode def homeAssistantMode(self, eq3ModeIn): switcher = { -1: "unknown", 0: "off", 1: "heat", 2: "auto", 3: "heat", 4: "off", 5: "heat" } return switcher.get(eq3ModeIn, "unknown") def PublishState(self): trvCurrentState = self.trvReadStateJSON() try: self.mqttClient.publish( self.mqttGateTopic + self.trvName + "/status", trvCurrentState) except: time.sleep(5) self.logger.error("Failed to publish state") try: self.mqttClient.publish( self.mqttGateTopic + self.trvName + "/status", trvCurrentState) except: self.logger.error("Failed to publish state") def ModeSet(self, newMode): try: self.trv.mode = self.eq3Mode(newMode) except: time.sleep(5) self.logger.error("SM1: Failed to communicate with " + self.trvName) try: self.trv.mode = self.eq3Mode(newMode) except: self.logger.error("SM2: Failed to communicate with " + self.trvName) self.PublishState() def TempSet(self, newTemp): if (float(str(newTemp)) <= 4.5): self.ModeSet("off") if (float(str(newTemp)) >= 30): self.ModeSet("on") try: self.trv.target_temperature = float(str(newTemp)) except: time.sleep(5) self.logger.error("ST1: Failed to communicate with " + self.trvName) try: self.trv.target_temperature = float(str(newTemp)) except: self.logger.error("ST2: Failed to communicate with " + self.trvName) self.PublishState()
def setUp(self): self.thermostat = Thermostat(_mac=None, connection_cls=FakeConnection)
class TestThermostat(TestCase): def setUp(self): self.thermostat = Thermostat(_mac=None, connection_cls=FakeConnection) def test__verify_temperature(self): with self.assertRaises(TemperatureException): self.thermostat._verify_temperature(-1) with self.assertRaises(TemperatureException): self.thermostat._verify_temperature(35) self.thermostat._verify_temperature(8) self.thermostat._verify_temperature(25) def test_parse_schedule(self): self.fail() def test_handle_notification(self): self.fail() def test_query_id(self): self.thermostat.query_id() self.assertEqual(self.thermostat.firmware_version, 120) self.assertEqual(self.thermostat.device_serial, "PEQ2130075") def test_update(self): th = self.thermostat th._conn.set_status('auto') th.update() self.assertEqual(th.valve_state, 0) self.assertEqual(th.mode, Mode.Auto) self.assertEqual(th.target_temperature, 20.0) self.assertFalse(th.locked) self.assertFalse(th.low_battery) self.assertFalse(th.boost) self.assertFalse(th.window_open) th._conn.set_status('manual') th.update() self.assertTrue(th.mode, Mode.Manual) th._conn.set_status('away') th.update() self.assertEqual(th.mode, Mode.Away) self.assertEqual(th.target_temperature, 17.5) self.assertEqual(th.away_end, datetime(2019, 3, 29, 23, 00)) th._conn.set_status('boost') th.update() self.assertTrue(th.boost) self.assertEqual(th.mode, Mode.Boost) def test_presets(self): th = self.thermostat self.thermostat._conn.set_status('presets') self.thermostat.update() self.assertEqual(th.window_open_temperature, 12.0) self.assertEqual(th.window_open_time, timedelta(minutes=15.0)) self.assertEqual(th.comfort_temperature, 20.0) self.assertEqual(th.eco_temperature, 17.0) self.assertEqual(th.temperature_offset, 0) def test_query_schedule(self): self.fail() def test_schedule(self): self.fail() def test_set_schedule(self): self.fail() def test_target_temperature(self): self.fail() def test_mode(self): self.fail() def test_mode_readable(self): self.fail() def test_boost(self): self.fail() def test_valve_state(self): th = self.thermostat th._conn.set_status('valve_at_22') th.update() self.assertEqual(th.valve_state, 22) def test_window_open(self): th = self.thermostat th._conn.set_status('window') th.update() self.assertTrue(th.window_open) def test_window_open_config(self): self.fail() def test_locked(self): self.fail() def test_low_battery(self): th = self.thermostat th._conn.set_status('low_batt') th.update() self.assertTrue(th.low_battery) def test_temperature_offset(self): self.fail() def test_activate_comfort(self): self.fail() def test_activate_eco(self): self.fail() def test_min_temp(self): self.fail() def test_max_temp(self): self.fail() def test_away_end(self): self.fail() def test_decode_mode(self): self.fail()
# IOBroker->Script - Setze den Temperaturwert im Thermostat def setEQTemp(sMAC, sTemp): thermostat = Thermostat(sMAC) thermostat.target_temperature = float(sTemp) thermostat.update() # Pruefe bei aufruf des Script die mitgegebenen Parameter if len(sys.argv) > 1: # Mehr als 1 Parameter, dann setze Temp im Thermostat sZimmer = sys.argv[1] sTemp = sys.argv[2] nArrID = aZimmer.index(sZimmer) setEQTemp(aMacs[nArrID], sTemp) else: # Kein Parameter, dann aktualisere Werte im IOBroker für jeden definierten Themostat for i in range(len(aMacs)): thermostat = Thermostat(aMacs[i]) thermostat.update() # Wenn Mode Closed gesetzt am Thermostat, dann setzen wird Mode Manual (5 Grad), sonst gibts Probleme if str(thermostat.mode) == "Mode.Closed": print("setmode") thermostat.mode = 3 thermostat.update() # Debug print(str(thermostat.mode) + " : " + str(thermostat.target_temperature) + " : " + str(thermostat.valve_state)) # Setze Werte im IOBroker setIOTemp(aZimmer[i],thermostat.target_temperature) setIOValve(aZimmer[i],thermostat.valve_state) setIOAlarm(aZimmer[i],thermostat.low_battery)
def setEQTemp(sMAC, sTemp): thermostat = Thermostat(sMAC) thermostat.target_temperature = float(sTemp) thermostat.update()
class Plugin(plugin.PluginProto): PLUGIN_ID = 516 PLUGIN_NAME = "Thermostat - BLE EQ3 (EXPERIMENTAL)" PLUGIN_VALUENAME1 = "TargetTemp" PLUGIN_VALUENAME2 = "Mode" PLUGIN_VALUENAME3 = "TempOffset" def __init__(self, taskindex): # general init plugin.PluginProto.__init__(self, taskindex) self.dtype = rpieGlobals.DEVICE_TYPE_BLE self.vtype = rpieGlobals.SENSOR_TYPE_TRIPLE self.valuecount = 3 self.senddataoption = True self.recdataoption = False self.timeroption = True self.timeroptional = False self.formulaoption = True self.thermostat = None self.readinprogress = False self.battery = 0 self._lastdataservetime = 0 self._nextdataservetime = 0 self.blestatus = None def webform_load(self): # create html page for settings webserver.addFormTextBox("Device Address", "plugin_516_addr", str(self.taskdevicepluginconfig[0]), 20) webserver.addFormNote( "Enable blueetooth then <a href='blescanner'>scan EQ3 address</a> first." ) webserver.addFormNote( "!!!This plugin WILL NOT work with ble scanner plugin!!!") return True def webform_save(self, params): # process settings post reply self.taskdevicepluginconfig[0] = str( webserver.arg("plugin_516_addr", params)).strip().lower() self.plugin_init() return True def plugin_init(self, enableplugin=None): plugin.PluginProto.plugin_init(self, enableplugin) self.readinprogress = 0 if self.enabled: try: self.blestatus = BLEHelper.BLEStatus[ 0] # 0 is hardwired in library self.blestatus.registerdataprogress( self.taskindex) # needs continous access except: pass self.ports = str(self.taskdevicepluginconfig[0]) try: self.thermostat = Thermostat( str(self.taskdevicepluginconfig[0])) self.initialized = True time.sleep(1) except: self.initialized = False if self.interval > 2: nextr = self.interval - 2 else: nextr = 0 self._lastdataservetime = rpieTime.millis() - (nextr * 1000) else: self.ports = "" def plugin_exit(self): try: self.blestatus.unregisterdataprogress(self.taskindex) if self.thermostat._conn is not None: self.thermostat._conn.__exit__() except: pass def plugin_read(self): result = False if self.enabled and self.initialized and self.readinprogress == 0: self.readinprogress = 1 try: self.thermostat.update() time.sleep(0.1) if self.thermostat.low_battery: self.battery = 10 else: self.battery = 100 self.set_value(1, float(self.thermostat.target_temperature), False) self.set_value(2, int(self.thermostat.mode), False) self.set_value(3, float(self.thermostat.temperature_offset), False, susebattery=self.battery) self.plugin_senddata(pusebattery=self.battery) self._lastdataservetime = rpieTime.millis() result = True except Exception as e: print("EQ3 read error: " + str(e)) time.sleep(3) self.readinprogress = 0 return result def plugin_write(self, cmd): # handle incoming commands res = False cmdarr = cmd.split(",") cmdarr[0] = cmdarr[0].strip().lower() if self.initialized == False: return False if cmdarr[0] == "eq3": try: rname = cmdarr[1].strip() except: rname = "" if rname.lower() != self.gettaskname().lower(): return False # command arrived to another task, skip it if cmdarr[2] == "sync": if self.thermostat is not None: try: self.thermostat.update() jstruc = { "target temperature": self.thermostat.target_temperature, "mode": self.thermostat.mode_readable } res = str(jstruc).replace("'", '"').replace(', ', ',\n') res = res.replace("{", "{\n").replace("}", "\n}") except Exception as e: print(e) return res elif cmdarr[2] == "mode": mode = "" try: mode = str(cmdarr[3].strip()).lower() except: mode = "" tmode = -1 if mode == "closed": tmode = Mode.Closed elif mode == "open": tmode = Mode.Open elif mode == "auto": tmode = Mode.Auto elif mode == "manual": tmode = Mode.Manual elif mode == "away": tmode = Mode.Away elif mode == "boost": tmode = Mode.Boost if (self.thermostat is not None) and int(tmode) > -1: try: self.thermostat.mode = tmode misc.addLog(rpieGlobals.LOG_LEVEL_DEBUG, "EQ3 mode " + str(mode)) res = True except Exception as e: print(e) elif cmdarr[2] == "temp": temp = -1 try: temp = misc.str2num(cmdarr[3].strip()) except: temp = -1 if (self.thermostat is not None) and temp > 4 and temp < 31: try: self.thermostat.target_temperature = temp misc.addLog(rpieGlobals.LOG_LEVEL_DEBUG, "EQ3 target temperature " + str(temp)) res = True except Exception as e: print(e) return res
class TestThermostat(TestCase): def setUp(self): self.thermostat = Thermostat(_mac=None, connection_cls=FakeConnection) def test__verify_temperature(self): with self.assertRaises(TemperatureException): self.thermostat._verify_temperature(-1) with self.assertRaises(TemperatureException): self.thermostat._verify_temperature(35) self.thermostat._verify_temperature(8) self.thermostat._verify_temperature(25) def test_parse_schedule(self): self.fail() def test_handle_notification(self): self.fail() def test_update(self): self.fail() def test_query_schedule(self): self.fail() def test_schedule(self): self.fail() def test_set_schedule(self): self.fail() def test_target_temperature(self): self.fail() def test_mode(self): self.fail() def test_mode_readable(self): self.fail() def test_boost(self): self.fail() def test_valve_state(self): self.fail() def test_window_open(self): self.fail() def test_window_open_config(self): self.fail() def test_locked(self): self.fail() def test_low_battery(self): self.fail() def test_temperature_offset(self): self.fail() def test_activate_comfort(self): self.fail() def test_activate_eco(self): self.fail() def test_min_temp(self): self.fail() def test_max_temp(self): self.fail() def test_away_end(self): self.fail() def test_decode_mode(self): self.fail()