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")
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
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)
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()
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))
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
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)
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)