예제 #1
0
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)
예제 #2
0
 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'
예제 #3
0
                 name=None,
                 homie_settings=None,
                 mqtt_settings=None):
        super().__init__(device_id, name, homie_settings, mqtt_settings)
        self.add_node(Node_EQ3BT(self))
        self.start()

    def update(self, thermostat):
        self.get_node('eq3bt').update_state(thermostat)


homie = Device_EQ3BT(device_id=device_id,
                     name=device_name,
                     mqtt_settings=mqtt_settings)

while True:
    try:
        thermostat.update()
    except Exception as e:
        l.warning(e)
        l.warning("Error connecting to thermostat; trying again")
        time.sleep(10)
        continue
    if thermostat._raw_mode == None:
        l.warning("No reply received from thermostat... Strange!")
        time.sleep(10)
        continue  #no message received within acceptable time...
    print(thermostat)
    homie.update(thermostat)
    time.sleep(300)
예제 #4
0
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()
예제 #5
0
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
예제 #6
0
def setEQTemp(sMAC, sTemp):
    thermostat = Thermostat(sMAC)
    thermostat.target_temperature = float(sTemp)
    thermostat.update()
예제 #7
0
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()