示例#1
0
class WaterSensor(ComponentSensor):
    DEBUG = False

    def __init__(self, adc, power_pin=None, cutoff_voltage=None, interval_publish=None,
                 interval_reading=1, mqtt_topic=None, friendly_name=None, discover=True,
                 expose_intervals=False, intervals_topic=None):
        interval_publish = interval_publish or -1
        global _unit_index
        _unit_index += 1
        super().__init__(COMPONENT_NAME, __version__, _unit_index, discover, interval_publish,
                         interval_reading, mqtt_topic, _log, expose_intervals, intervals_topic)
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT) if power_pin is not None else None
        self._cv = cutoff_voltage or self._adc.maxVoltage()
        self._lv = None
        self._addSensorType(SENSOR_BINARY_MOISTURE, 0, 0, VALUE_TEMPLATE, "", friendly_name,
                            mqtt_topic, None, True)
        self._pub_coro = None

    async def _read(self):
        a = time.ticks_us()
        p = self._ppin
        if p is not None:
            p.value(1)
        vol = self._adc.readVoltage()
        if self.DEBUG is True:
            print("#{!s}, V".format(self.getTopic(SENSOR_BINARY_MOISTURE)[-1]), vol)
        if p is not None:
            p.value(0)
        if vol >= self._cv:
            state = False
            if self._lv != state:
                # dry
                if self._pub_coro is not None:
                    asyncio.cancel(self._pub_coro)
                self._pub_coro = _mqtt.publish(self.getTopic(SENSOR_BINARY_MOISTURE), "OFF", qos=1,
                                               retain=True, timeout=None, await_connection=True)
                asyncio.get_event_loop().create_task(self._pub_coro)
            self._lv = state
        else:
            state = True
            if self._lv != state:
                # wet
                if self._pub_coro is not None:
                    asyncio.cancel(self._pub_coro)
                self._pub_coro = _mqtt.publish(self.getTopic(SENSOR_BINARY_MOISTURE), "ON", qos=1,
                                               retain=True, timeout=None, await_connection=True)
                asyncio.get_event_loop().create_task(self._pub_coro)
            self._lv = state
        b = time.ticks_us()
        if WaterSensor.DEBUG:
            print("Water measurement took", (b - a) / 1000, "ms")
示例#2
0
class EC(Component):
    DEBUG = False

    def __init__(self, r1, ra, adc, power_pin, ppm_conversion, temp_coef, k, temp_sensor,
                 precision_ec=3, interval=None, topic_ec=None, topic_ppm=None,
                 friendly_name_ec=None, friendly_name_ppm=None):
        super().__init__()
        self._interval = interval or config.INTERVAL_SEND_SENSOR
        self._prec_ec = int(precision_ec)
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT)
        self._r1 = r1
        self._ra = ra
        self._ppm_conversion = ppm_conversion
        self._temp_coef = temp_coef
        self._k = k
        self._temp = temp_sensor
        if hasattr(temp_sensor, "temperature") is False:
            raise AttributeError(
                "Temperature sensor {!s}, type {!s} has no async method temperature()".format(temp_sensor,
                                                                                              type(temp_sensor)))
        gc.collect()
        self._ec25 = None
        self._ppm = None
        self._time = 0
        # This makes it possible to use multiple instances of MySensor
        global _count
        self._count = _count
        _count += 1
        self._topic_ec = topic_ec or _mqtt.getDeviceTopic("{!s}/{!s}".format("EC", self._count))
        self._topic_ppm = topic_ppm or _mqtt.getDeviceTopic("{!s}/{!s}".format("PPM", self._count))
        self._frn_ec = friendly_name_ec
        self._frn_ppm = friendly_name_ppm

    async def _init(self):
        await super()._init()
        gen = self._read
        interval = self._interval
        while True:
            await gen()
            await asyncio.sleep(interval)

    async def _discovery(self):
        sens = '"unit_of_meas":"mS",' \
               '"val_tpl":"{{ value|float }}",'
        name = "{!s}{!s}{!s}".format(_component_name, self._count, "EC25")
        await self._publishDiscovery(_component_type, self._topic_ec, name, sens, self._frn_ec or "EC25")
        del sens, name
        gc.collect()
        sens = '"unit_of_meas":"ppm",' \
               '"val_tpl":"{{ value|int }}",'
        name = "{!s}{!s}{!s}".format(_component_name, self._count, "PPM")
        await self._publishDiscovery(_component_type, self._topic_ppm, name, sens, self._frn_ppm or "PPM")
        del sens, name
        gc.collect()

    async def _read(self, publish=True):
        if time.ticks_ms() - self._time < 5000:
            self._time = time.ticks_ms()
            await asyncio.sleep(5)
        temp = await self._temp.temperature(publish=False)
        if temp is None:
            await asyncio.sleep(3)
            temp = await self._temp.temperature(publish=False)
            if temp is None:
                _log.warn("Couldn't get temperature, aborting EC measurement")
                self._ec25 = None
                self._ppm = None
                return None, None
        self._ppin.value(1)
        vol = self._adc.readVoltage()
        # vol = self._adc.readVoltage()
        # micropython on esp is probably too slow to need this. It was intended for arduino
        if self.DEBUG is True:
            print("Temp", temp)
            print("V", vol)
        self._ppin.value(0)
        if vol >= self._adc.maxVoltage():
            ec25 = 0
            ppm = 0
            await _log.asyncLog("warn", "Cable not in fluid")
        else:
            if vol <= 0.5:
                _log.warn("Voltage <=0.5, change resistor")
            rc = (vol * (self._r1 + self._ra)) / (self._adc.maxVoltage() - vol)
            # rc = rc - self._ra # sensor connected to ground, not a pin, therefore no Ra
            ec = 1000 / (rc * self._k)
            ec25 = ec / (1 + self._temp_coef * (temp - 25.0))
            ppm = int(ec25 * self._ppm_conversion * 1000)
            ec25 = round(ec25, self._prec_ec)
            if self.DEBUG:
                print("Rc", rc)
                print("EC", ec)
                print("EC25", ec25, "MilliSimens")
                print("PPM", ppm)
        self._ec25 = ec25
        self._ppm = ppm
        self._time = time.ticks_ms()

        if publish:
            await _mqtt.publish(self._topic_ec, ("{0:." + str(self._prec_ec) + "f}").format(ec25))
            await _mqtt.publish(self._topic_ppm, ppm)
        return ec25, ppm

    async def ec(self, publish=True):
        if time.ticks_ms() - self._time > 5000:
            await self._read(publish)
        return self._ec25

    async def ppm(self, publish=True):
        if time.ticks_ms() - self._time > 5000:
            await self._read(publish)
        return self._ppm
示例#3
0
class Amux:
    def __init__(self, s0, s1, s2, s3=None, mux=None, sig=None, return_voltages=False):
        """ It is possibile to initialize with:
            - pin numbers (or string on esp8266)
            - mux object and pin numbers (of mux pins)
            - Pin objects (either from machine or mux Pin objects [no mux object needed])
            :type return_voltages: bool, True returns voltages on .read() else raw adc value
            :type mux: Mux object if a multiplexer is used
            :type sig: ADC pin number (esp32) or None (esp8266)
            Amux uses default return values of ADC in .read()
            --> On esp8266 raw, on esp32_LoBo voltage
            s3 is optional, only needed if 16 pins are used, 8 pins possible with s0-s2.
            Amux can be read like a list: value=amux[2]
        """
        if mux:
            self.s0 = s0
            self.s1 = s1
            self.s2 = s2
            self.s3 = s3
            self.mux = mux
        else:
            if type(s0) in (int, str):
                self.s0 = Pin(s0 if type(s0) != str else config.pins[s0], Pin.OUT)
                self.s1 = Pin(s1 if type(s1) != str else config.pins[s1], Pin.OUT)
                self.s2 = Pin(s2 if type(s2) != str else config.pins[s2], Pin.OUT)
                if s3:
                    self.s3 = Pin(s3 if type(s3) != str else config.pins[s3], Pin.OUT)
            else:
                self.s0 = s0
                self.s1 = s1
                self.s2 = s2
                if s3:
                    self.s3 = s3
        if s3:
            self.__size = 16
        else:
            self.__size = 8
        self._return_voltages = return_voltages
        self.sig = ADC(0 if sig is None and platform == "esp8266" else sig)  # unified ADC interface

    def setReturnVoltages(self, vol):
        self._return_voltages = vol

    def __getitem__(self, a):
        return self.read(a)

    def getSize(self):
        """ Get number of pins"""
        return self.__size

    def read(self, a, return_voltage=None):
        if a >= self.__size:
            raise ValueError("Maximum Port number is {!s}".format(self.__size - 1))
        if type(self.s0) == int:  # mux pins
            if self.__size == 16:
                self.mux[self.s3] = (1 if a & 8 else 0)
            self.mux[self.s2] = (1 if a & 4 else 0)
            self.mux[self.s1] = (1 if a & 2 else 0)
            self.mux[self.s0] = (1 if a & 1 else 0)
            self.mux.write()
        else:
            if self.__size == 16:
                self.s3.value(1 if a & 8 else 0)
            self.s2.value(1 if a & 4 else 0)
            self.s1.value(1 if a & 2 else 0)
            self.s0.value(1 if a & 1 else 0)
        if return_voltage is True or return_voltage is None and self._return_voltages is True:
            return self.sig.readVoltage()
        else:
            return self.sig.readRaw()

    def readVoltage(self, a):
        return self.read(a, return_voltage=True)

    def readRaw(self, a):
        return self.read(a, return_voltage=False)

    def ADC(self, i, *args, **kwargs):
        """ compatible to machine.ADC, returns an ADC object"""
        return ADC(self, i)
示例#4
0
class Battery(Component):
    def __init__(self,
                 adc,
                 voltage_max,
                 voltage_min,
                 multiplier_adc,
                 cutoff_pin=None,
                 precision_voltage=2,
                 interval_watching=1,
                 interval=None,
                 mqtt_topic=None,
                 friendly_name=None,
                 friendly_name_abs=None):
        super().__init__()
        self._interval = interval or config.INTERVAL_SEND_SENSOR
        self._interval_watching = interval_watching
        self._topic = mqtt_topic or _mqtt.getDeviceTopic(_component_name)
        self._precision = int(precision_voltage)
        self._adc = ADC(adc)  # unified ADC interface
        self._voltage_max = voltage_max
        self._voltage_min = voltage_min
        self._multiplier = multiplier_adc
        self._cutoff_pin = None if cutoff_pin is None else (Pin(
            cutoff_pin, machine.Pin.OUT))
        if self._cutoff_pin is not None:
            self._cutoff_pin.value(0)
        self._frn = friendly_name
        self._frn_abs = friendly_name_abs
        gc.collect()
        self._event_low = None
        self._event_high = None

    def getVoltageMax(self):
        """Getter for consumers"""
        return self._voltage_max

    def getVoltageMin(self):
        """Getter for consumers"""
        return self._voltage_min

    async def _read(self, publish=True):
        try:
            value = self._adc.readVoltage()
        except Exception as e:
            _log.error("Error reading sensor {!s}: {!s}".format(
                _component_name, e))
            return None
        if value is not None:
            value *= self._multiplier
            value = round(value, self._precision)
        if value is None:
            _log.warn("Sensor {!s} got no value".format(_component_name))
        elif publish:
            await _mqtt.publish(self._topic, ("{0:." + str(self._precision) +
                                              "f}").format(value))
        return value

    async def voltage(self, publish=True):
        return await self._read(publish=publish)

    async def _init(self):
        await super()._init()
        interval = self._interval
        interval_watching = self._interval_watching
        t = time.ticks_ms()
        while True:
            # reset enets on next reading so consumers don't need to do it as there might be multiple consumers awaiting
            if self._event_low is not None:
                self._event_low.release()
            if self._event_high is not None:
                self._event_high.release()
            if time.ticks_ms() > t:
                # publish interval
                voltage = self._read()
                t = time.ticks_ms() + interval
            else:
                voltage = self._read(publish=False)
            if voltage > self._voltage_max:
                if self._event_high is not None:
                    self._event_high.set(data=voltage)
                    # no log as consumer has to take care of logging or doing something
                else:
                    _log.warn(
                        "Battery voltage of {!s} exceeds maximum of {!s}".
                        format(voltage, self._voltage_max))
            elif voltage < self._voltage_min:
                if self._event_low is not None:
                    self._event_low.set(data=voltage)
                    # no log as consumer has to take care of logging or doing something
                else:
                    _log.warn(
                        "Battery voltage of {!s} lower than minimum of {!s}".
                        format(voltage, self._voltage_min))
                if self._cutoff_pin is not None:
                    if self._cutoff_pin.value() == 1:
                        _log.critical("Cutting off power did not work!")
                        self._cutoff_pin.value(0)  # trying again
                        await asyncio.sleep(1)
                    else:
                        _log.warn("Cutting off power")
                    await asyncio.sleep(
                        5
                    )  # time to send all logs and for consumers to get done
                    self._cutoff_pin.value(1)
            await asyncio.sleep(interval_watching)

    def registerEventHigh(self, event):
        self._event_high = event

    def registerEventLow(self, event):
        self._event_low = event

    async def _discovery(self):
        sens = DISCOVERY_SENSOR.format(
            "battery",  # device_class
            "%",  # unit_of_measurement
            "{{ value_json.relative }}")  # value_template
        await self._publishDiscovery(_component_type, self._topic,
                                     _component_name + "r", sens, self._frn
                                     or "Battery %")
        sens = '"unit_of_meas":"V",' \
               '"val_tpl":"{{ value_json.absolute }}",' \
               '"ic":"mdi:car-battery"'
        await self._publishDiscovery(_component_type, self._topic,
                                     _component_name + "a", sens, self._frn_abs
                                     or "Battery Volt")
        del sens
        gc.collect()
示例#5
0
class PHsensor(Component):
    def __init__(self,
                 adc,
                 adc_multi,
                 voltage_calibration_0,
                 pH_calibration_value_0,
                 voltage_calibration_1,
                 pH_calibration_value_1,
                 precision=2,
                 interval=None,
                 mqtt_topic=None,
                 friendly_name=None,
                 discover=True):
        # This makes it possible to use multiple instances of MySensor
        global _unit_index
        _unit_index += 1
        super().__init__(COMPONENT_NAME, __version__, _unit_index, discover)
        self._interval = interval or config.INTERVAL_SENSOR_PUBLISH
        self._topic = mqtt_topic
        self._frn = friendly_name
        self._adc = ADC(adc)
        self._adc_multi = adc_multi

        self.__ph = None

        self._prec = int(precision)

        self._v0 = voltage_calibration_0
        self._v1 = voltage_calibration_1
        self._ph0 = pH_calibration_value_0
        self._ph1 = pH_calibration_value_1
        gc.collect()
        if self._interval > 0:  # if interval==-1 no loop will be started
            asyncio.get_event_loop().create_task(self._loop())

    async def _loop(self):
        interval = self._interval
        while True:
            self.__ph = await self._read()
            await asyncio.sleep(interval)

    async def _discovery(self, register=True):
        name = "{!s}{!s}".format(COMPONENT_NAME, self._count)
        if register:
            await self._publishDiscovery(_COMPONENT_TYPE, self.acidityTopic(),
                                         name, PH_TYPE, self._frn or "pH")
        else:
            await self._deleteDiscovery(_COMPONENT_TYPE, name)

    async def _read(self, publish=True, timeout=5) -> float:
        buf = []
        for _ in range(10):
            buf.append(self._adc.readVoltage() * self._adc_multi)
            await asyncio.sleep_ms(50)
        buf.remove(max(buf))
        buf.remove(max(buf))
        buf.remove(min(buf))
        buf.remove(min(buf))
        v = 0
        for i in range(len(buf)):
            v += buf[i]
        v /= len(buf)
        ph1 = self._ph1
        ph0 = self._ph0
        v0 = self._v0
        v1 = self._v1
        m = (ph1 - ph0) / (v1 - v0)
        b = (ph0 * v1 - ph1 * v0) / (v1 - v0)
        print("U", v)
        print("m", m)
        print("b", b)
        value = m * v + b
        value = round(value, self._prec)
        print("pH", value)
        if value > 14:
            await _log.asyncLog(
                "error",
                "Not correctly connected, voltage {!s}, ph {!s}".format(
                    v, value))
            return None
        if publish:
            await _mqtt.publish(self.acidityTopic(),
                                ("{0:." + str(self._prec) +
                                 "f}").format(value),
                                timeout=timeout,
                                await_connection=False)
        return value

    async def acidity(self, publish=True, timeout=5, no_stale=False) -> float:
        if self._interval == -1 or no_stale:
            return await self._read(publish, timeout)
        return self.__ph

    @staticmethod
    def acidityTemplate():
        """Other components like HVAC might need to know the value template of a sensor"""
        return _VAL_T_ACIDITY

    def acidityTopic(self):
        return self._topic or _mqtt.getDeviceTopic("{!s}{!s}".format(
            COMPONENT_NAME, self._count))
示例#6
0
class Battery(ComponentSensor):
    def __init__(self, adc, voltage_max: float, voltage_min: float, multiplier_adc: float,
                 cutoff_pin=None, precision_voltage: int = 2, interval_reading: float = 1,
                 interval_publish: float = None, mqtt_topic: str = None, friendly_name: str = None,
                 friendly_name_abs: str = None, discover: bool = True,
                 expose_intervals: bool = False, intervals_topic: str = None):
        global _unit_index
        _unit_index += 1
        super().__init__(COMPONENT_NAME, __version__, _unit_index, discover, interval_publish,
                         interval_reading, mqtt_topic, _log, expose_intervals, intervals_topic)
        self._adc = ADC(adc)  # unified ADC interface
        self._voltage_max = voltage_max
        self._voltage_min = voltage_min
        self._multiplier = multiplier_adc
        self._cutoff_pin = None if cutoff_pin is None else (Pin(cutoff_pin, machine.Pin.OUT))
        if self._cutoff_pin is not None:
            self._cutoff_pin.value(0)
        self._event_low = None
        self._event_high = None
        self._addSensorType(SENSOR_BATTERY, 2, 0, _VAL_T_CHARGE, "%", friendly_name_abs)
        self._addSensorType("voltage", precision_voltage, 0, _VAL_T_VOLTAGE, "V", friendly_name,
                            None, _TYPE_VOLTAGE)
        asyncio.get_event_loop().create_task(self._events())
        gc.collect()

    def getVoltageMax(self) -> float:
        """Getter for consumers"""
        return self._voltage_max

    def getVoltageMin(self) -> float:
        """Getter for consumers"""
        return self._voltage_min

    async def _read(self):
        try:
            value = self._adc.readVoltage()
        except Exception as e:
            await _log.asyncLog("error", "Error reading sensor:", e, timeout=10)
        else:
            value *= self._multiplier
            await self._setValue("voltage", value)  # applies rounding and saves value
            value = await self.getValue("voltage")
            if value:
                value = (value - self._voltage_min) / (self._voltage_max - self._voltage_min)
                await self._setValue(SENSOR_BATTERY, value)

    async def _events(self):
        ev = self.getReadingsEvent()
        while True:
            # reset events on next reading so consumers don't need to do it as there
            # might be multiple consumers awaiting
            if self._event_low is not None:
                self._event_low.clear()
            if self._event_high is not None:
                self._event_high.clear()
            await ev
            voltage = await self.getValue("voltage")
            ev.clear()
            if voltage > self._voltage_max:
                if self._event_high is not None:
                    self._event_high.set(data=voltage)
                    # no log as consumer has to take care of logging or doing something
                else:
                    await _log.asyncLog("warn", "Battery voltage of", voltage,
                                        "exceeds maximum of",
                                        self._voltage_max, timeout=5)
            elif voltage < self._voltage_min:
                if self._event_low is not None:
                    self._event_low.set(data=voltage)
                    # no log as consumer has to take care of logging or doing something
                else:
                    await _log.asyncLog("warn", "Battery voltage of", voltage,
                                        "lower than minimum of", self._voltage_min, timeout=5)
                if self._cutoff_pin is not None:
                    if self._cutoff_pin.value() == 1:
                        await _log.asyncLog("critical", "Cutting off power did not work!",
                                            timeout=5)
                        self._cutoff_pin.value(0)  # trying again
                        await asyncio.sleep(1)
                    else:
                        await _log.asyncLog("warn", "Cutting off power", timeout=5)
                    await asyncio.sleep(5)  # time to send all logs and for consumers to get done
                    self._cutoff_pin.value(1)

    def registerEventHigh(self, event):
        self._event_high = event

    def registerEventLow(self, event):
        self._event_low = event
示例#7
0
class WaterSensor(Component):
    DEBUG = False

    def __init__(self, adc, power_pin=None, cutoff_voltage=None, interval=None,
                 interval_reading=1, topic=None, friendly_name=None):
        super().__init__()
        self._ir = interval_reading
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT) if power_pin is not None else None
        self._cv = cutoff_voltage or self._adc.maxVoltage()
        global _instances
        _instances.append(self)
        global _count
        self._t = topic or _mqtt.getDeviceTopic("waterSensor/{!s}".format(_count))
        self._count = _count
        _count += 1
        self._lv = None
        self._tm = time.ticks_ms()
        interval = interval or config.INTERVAL_SEND_SENSOR
        self._int = interval * 1000
        self._frn = friendly_name

    async def _init(self):
        await super()._init()
        if self._count == 0:  # only the first sensor reads all sensors to prevent uasyncio queue overflow
            interval_reading = self._ir - 0.05 * len(_instances)
            if interval_reading < 0:
                interval_reading = 0
                # still has 100ms after every read
            while True:
                for inst in _instances:
                    a = time.ticks_us()
                    await inst.water()
                    b = time.ticks_us()
                    if WaterSensor.DEBUG:
                        print("Water measurement took", (b - a) / 1000, "ms")
                    await asyncio.sleep_ms(50)
                    # using multiple sensors connected to Arduinos it would result in long blocking calls
                    # because a single call to a pin takes ~17ms
                await asyncio.sleep(interval_reading)

    async def _discovery(self):
        name = "{!s}{!s}".format(_component_name, self._count)
        sens = DISCOVERY_BINARY_SENSOR.format("moisture")  # device_class
        await self._publishDiscovery(_component_type, self._t, name, sens, self._frn or "Moisture")
        gc.collect()

    async def _read(self, publish=True):
        p = self._ppin
        if p is not None:
            p.value(1)
        vol = self._adc.readVoltage()
        if self.DEBUG is True:
            print("#{!s}, V".format(self._t[-1]), vol)
        if p is not None:
            p.value(0)
        if vol >= self._cv:
            state = False
            if publish is True and (time.ticks_diff(time.ticks_ms(), self._tm) > self._int or self._lv != state):
                await _mqtt.publish(self._t, "OFF", qos=1, retain=True)  # dry
                self._tm = time.ticks_ms()
            self._lv = state
            return False
        else:
            state = True
            if publish is True and (time.ticks_diff(time.ticks_ms(), self._tm) > self._int or self._lv != state):
                await _mqtt.publish(self._t, "ON", qos=1, retain=True)  # wet
                self._tm = time.ticks_ms()
            self._lv = state
            return True

    async def water(self, publish=True):
        return await self._read(publish)
示例#8
0
class Moisture(Component):
    def __init__(self,
                 adc_pin,
                 water_voltage,
                 air_voltage,
                 sensor_types,
                 power_pin=None,
                 power_warmup=None,
                 publish_converted_value=False,
                 mqtt_topic=None,
                 interval=None,
                 friendly_name=None,
                 friendly_name_cv=None):
        super().__init__()
        self._adc = ADC(adc_pin)
        if power_pin is None:
            self._ppin = None
        else:
            if type(power_pin) == list:
                self._ppin = []
                for pin in power_pin:
                    self._ppin.append(Pin(pin, machine.Pin.OUT))
            else:
                self._ppin = Pin(power_pin, machine.Pin.OUT)
        self._power_warmup = power_warmup or None if power_pin is None else 10
        self._sensor_types = sensor_types
        if isinstance(
                self._adc, pyADC
        ):  # pyADC provides unified single ADC interface, not AMUX
            raise TypeError("Single ADC (no Amux) can't have multiple sensors")
        self._water_v = water_voltage
        self._air_v = air_voltage
        if type(sensor_types) == list:
            if type(water_voltage) != list or type(air_voltage) != list:
                raise TypeError(
                    "Voltages have to be lists with multiple sensor_types")
        self._pub_cv = publish_converted_value
        self._topic = mqtt_topic or _mqtt.getDeviceTopic("moisture")
        self._interval = interval or config.INTERVAL_SEND_SENSOR
        self._lock = Lock()
        self._frn = friendly_name
        self._frn_cv = friendly_name_cv
        gc.collect()

    async def _init(self):
        await super()._init()
        while True:
            await self.humidity()
            await asyncio.sleep(self._interval)

    def _getConverted(self, sensor_type, voltage):
        if voltage is None:
            return None
        air_voltage = self._air_v if type(
            self._air_v) != list else self._air_v[sensor_type]
        water_voltage = self._water_v if type(
            self._water_v) != list else self._water_v[sensor_type]
        if sensor_type == 0:  # std sensor
            if voltage > (water_voltage - air_voltage) / 2 + air_voltage:
                return "ON"  # wet
            else:
                return "OFF"  # dry
        elif sensor_type == 1:  # capacitive
            if voltage > air_voltage - (air_voltage - water_voltage) / 2:
                return "OFF"  # dry
            else:
                return "ON"  # wet
        else:
            raise NotImplementedError(
                "Sensor type {!s} not implemented".format(sensor_type))

    def _getPercentage(self, sensor_type, voltage):
        if voltage is None:
            return None
        air_voltage = self._air_v if type(
            self._air_v) != list else self._air_v[sensor_type]
        water_voltage = self._water_v if type(
            self._water_v) != list else self._water_v[sensor_type]
        if sensor_type == 0:  # std sensor:
            diff = water_voltage - air_voltage
            if voltage < air_voltage:
                return 0
            elif voltage > water_voltage:
                return 100
            return round((voltage - air_voltage) / diff * 100)
        elif sensor_type == 1:  # capacitive
            diff = air_voltage - water_voltage
            if voltage > air_voltage:
                return 0
            elif voltage < water_voltage:
                return 100
            return round((diff - (voltage - water_voltage)) / diff * 100)
        else:
            raise NotImplementedError(
                "Sensor type {!s} not implemented".format(sensor_type))

    async def _read(self, publish=True) -> list:
        res = []
        i = 0
        amux = not isinstance(self._adc, pyADC)
        async with self._lock:
            if type(self._sensor_types) == list:
                sensors = self._sensor_types
            elif amux is True:
                sensors = [self._sensor_types] * self._adc.getSize()
            else:
                sensors = [self._sensor_types]
            for sensor in sensors:
                if self._ppin is not None:
                    if type(self._ppin) == list:
                        self._ppin[sensor].value(1)
                    else:
                        self._ppin.value(1)
                    await asyncio.sleep_ms(self._power_warmup)
                voltage = None
                if sensor is None:
                    res.append(None)
                else:
                    voltage = 0
                    for j in range(3):
                        voltage += self._adc.readVoltage(
                            i) if amux else self._adc.readVoltage()
                    voltage /= 3
                    res.append(self._getPercentage(sensor, voltage))
                if publish:
                    await _mqtt.publish(self._topic + "/" + str(i), res[-1])
                    if self._pub_cv:
                        await _mqtt.publish(
                            self._topic + "/" + str(i) + "/conv",
                            self._getConverted(sensor, voltage))
                if self._ppin is not None:
                    if type(self._ppin) == list:
                        self._ppin[sensor].value(0)
                    else:
                        self._ppin.value(0)
                gc.collect()
                i += 1
        if len(res) == 0:
            return [None] * len(sensors)
        return res

    async def _discovery(self):
        amux = not isinstance(self._adc, pyADC)
        if type(self._sensor_types) == list:
            im = len(self._sensor_types)
        elif amux is True:
            im = self._adc.getSize()
        else:
            im = 1
        for i in range(im):
            if self._pub_cv:
                name = "{!s}{!s}CV".format(_component_name, i)
                sens = DISCOVERY_BINARY_SENSOR.format(
                    "moisture")  # device_class
                t = "{!s}/{!s}/conv".format(self._topic, i)
                await self._publishDiscovery("binary_sensor", t, name, sens,
                                             self._frn_cv or "Moisture")
            name = "{!s}{!s}".format(_component_name, i)
            t = "{!s}/{!s}".format(self._topic, i)
            sens = DISCOVERY_SENSOR.format(
                "humidity",  # device_class
                "%",  # unit_of_measurement
                "{{ value|float }}")  # value_template
            await self._publishDiscovery(_component_type, t, name, sens,
                                         self._frn or "Moisture rel.")
            del name, sens, t
            gc.collect()

    async def humidity(self, publish=True) -> list:
        """
        Returns a list of all sensor values.
        Does currently not conform to new API definitions.
        """
        return await self._read(publish=publish)