from waterlevel_sensor import WaterLevelSensor import bme280 import sh1106 from homie.constants import BOOLEAN, FALSE, TRUE, FLOAT from homie.property import HomieProperty from homie.node import HomieNode from homie.device import HomieDevice, await_ready_state from machine import Pin, ADC, I2C import settings import uasyncio as asyncio from time import ticks_ms, ticks_add, ticks_diff import gc class PlantNode(HomieNode): def __init__( self, name="Planze", pin_watering=Pin(13, Pin.OUT, value=0), pin_moisture=Pin(34), pin_water_tank=Pin(23), i2c=I2C(scl=Pin(18), sda=Pin(19)), interval=60*5, interval_watering=0.1): super().__init__(id="plant", name=name, type="watering") self.i2c = i2c self.display = sh1106.SH1106_I2C( i2c=i2c, width=128, height=64) # Update Interval self.interval = interval self.interval_watering = interval_watering self.interval_changed = False self.property_interval = HomieProperty( id="update_interval", name="Aktualisierungsrate", datatype=FLOAT, # TODO ISO8601 settable=True, on_message=self._set_interval, unit="s", ) self.add_property(self.property_interval) # WaterLevelSensor self.waterlevel_sensor = WaterLevelSensor() self.property_waterlevel = HomieProperty( id="waterlevel", name="Wassertankstand", datatype=FLOAT, unit="L", ) self.add_property(self.property_waterlevel) self.property_waterlevel_percent = HomieProperty( id="waterlevel_percent", name="Wassertankstand [%]", datatype=FLOAT, format="0.00:100.00", unit="%", ) self.add_property(self.property_waterlevel_percent) # self.property_waterlevel_max_value = HomieProperty( # id="waterlevel_max", # name="Wassertankstandsensor Max-Wert", # settable=True, # datatype=FLOAT, # default=1, # unit="#", # on_message=self._set_waterlevel_max_value # ) # self.add_property(self.property_waterlevel_max_value) # self.property_waterlevel_min_value = HomieProperty( # id="waterlevel_min", # name="Wassertankstandsensor Min-Wert", # settable=True, # datatype=FLOAT, # default=0, # unit="#", # on_message=self._set_waterlevel_min_value # ) # self.add_property(self.property_waterlevel_min_value) self.property_waterlevel_volume_liter = HomieProperty( id="waterlevel_volume_max", name="Wassertankgröße", settable=True, datatype=FLOAT, unit="L", on_message=self._set_waterlevel_volume ) self.add_property(self.property_waterlevel_volume_liter) # Moisture self.adc = ADC(pin_moisture) self.adc.atten(ADC.ATTN_11DB) self.property_moisture = HomieProperty( id="moisture", name="Feuchte", datatype=FLOAT, format="0.00:100.00", unit="%", ) self.add_property(self.property_moisture) # BMP280 self.bmp280 = bme280.BME280(i2c=self.i2c) self.property_temerature = HomieProperty( id="temperature", name="Temperatur", datatype=FLOAT, unit="°C", ) self.add_property(self.property_temerature) self.property_pressure = HomieProperty( id="pressure", name="Druck", datatype=FLOAT, unit="Pa", ) self.add_property(self.property_pressure) # Watering Motor self.pin_watering_motor = pin_watering self.pin_watering_motor.init(mode=Pin.OUT, value=0) self.property_water_power = HomieProperty( id="power", name="Bewässerung", settable=True, datatype=BOOLEAN, default=FALSE, on_message=self.toggle_motor, ) self.add_property(self.property_water_power) self.property_watering_max_duration = HomieProperty( id="watering_duration_max", name="Bewässerungszeit", settable=True, datatype=FLOAT, default=3, unit="s", ) self.add_property(self.property_watering_max_duration) asyncio.create_task(self.update_data()) @await_ready_state async def update_data(self): while True: self.property_moisture.value = "{:1.2f}".format( (4096 - self.adc.read()) / 40.96) self.property_waterlevel_percent.value = "{:1.0f}".format( self.waterlevel_sensor.level_percent) self.property_waterlevel.value = "{:1.2f}".format( self.waterlevel_sensor.level) self.property_waterlevel_volume_liter.value = "{:1.4f}".format( self.waterlevel_sensor.volume) self.property_pressure.value = "{:1.0f}".format( self.bmp280.pressure * 100) # hPa = 100 Pa self.property_temerature.value = "{:1.2f}".format( self.bmp280.temperature) # TODO ISO8601 self.property_interval.value = "PT{:1.3f}S".format(self.interval) self.property_interval.value = "{:1.3f}".format(self.interval) # We don't simply wait the update interval, as it can change while waiting. last_update = ticks_ms() wait_till = ticks_add(last_update, int(self.interval * 1000.0)) while ticks_diff(ticks_ms(), wait_till) < 0: watering = self.pin_watering_motor.value() == 1 if watering or self.interval_changed: self.interval_changed = False wait_till = ticks_add( last_update, int(self.interval if not watering else self.interval_watering) * 1000) sleep_for = min(int(self.interval_watering * 1000.0), ticks_diff(wait_till, ticks_ms())) await asyncio.sleep_ms(sleep_for) def toggle_motor(self, topic, payload, retained): ONOFF = {FALSE: 0, TRUE: 1} v = ONOFF[payload] self.pin_watering_motor(v) if v == 1: asyncio.create_task(self.stop_motor()) def stop_motor(self): await asyncio.sleep_ms( int(float(self.property_watering_max_duration.value) * 1000)) self.pin_watering_motor.value(0) self.property_water_power.value = FALSE def _set_waterlevel_min_value(self, topic, payload, retained): self.waterlevel_sensor.value_min = float(payload) def _set_waterlevel_max_value(self, topic, payload, retained): self.waterlevel_sensor.value_max = float(payload) def _set_waterlevel_volume(self, topic, payload, retained): self.waterlevel_sensor.volume = float(payload) def _set_interval(self, topic, payload, retained): self.interval = float(payload) self.interval_changed = True def main(): global plantNode # Homie device setup plantNode = PlantNode() homie = HomieDevice(settings) homie.add_node(plantNode) # run forever homie.run_forever() if __name__ == "__main__": main()