def __init__(
        self,
        display_channel,
        sensor_channel,
        pump_channel,
        water_level=0.5,
        alarm_level=0.5,
        pump_speed=0.7,
        pump_time=0.7,
        watering_delay=30,
        wet_point=0.7,
        dry_point=26.7,
        icon=None,
        auto_water=False,
        enabled=False,
    ):
        self.channel = display_channel
        self.sensor = Moisture(sensor_channel)
        self.pump = Pump(pump_channel)
        self.water_level = water_level
        self.alarm_level = alarm_level
        self.auto_water = auto_water
        self.pump_speed = pump_speed
        self.pump_time = pump_time
        self.watering_delay = watering_delay
        self.wet_point = wet_point
        self.dry_point = dry_point
        self.last_dose = time.time()
        self.icon = icon
        self.enabled = enabled
        self.alarm = False

        self.sensor.set_wet_point(wet_point)
        self.sensor.set_dry_point(dry_point)
示例#2
0
def test_moisture_setup(GPIO, smbus):
    from grow.moisture import Moisture

    ch1 = Moisture(channel=1)
    ch2 = Moisture(channel=2)
    ch3 = Moisture(channel=3)

    assert GPIO.setup.has_calls([
        mock.call(ch1._gpio_pin, GPIO.IN),
        mock.call(ch2._gpio_pin, GPIO.IN),
        mock.call(ch3._gpio_pin, GPIO.IN)
    ])
示例#3
0
    def __init__(
        self,
        display_channel,
        sensor_channel,
        pump_channel,
        title=None,
        water_level=0.5,
        warn_level=0.5,
        pump_speed=0.5,
        pump_time=0.2,
        watering_delay=60,
        wet_point=0.7,
        dry_point=26.7,
        icon=None,
        auto_water=False,
        enabled=False,
    ):
        self.channel = display_channel
        self.sensor = Moisture(sensor_channel)
        self.pump = Pump(pump_channel)
        self.water_level = water_level
        self.warn_level = warn_level
        self.auto_water = auto_water
        self.pump_speed = pump_speed
        self.pump_time = pump_time
        self.watering_delay = watering_delay
        self._wet_point = wet_point
        self._dry_point = dry_point
        self.last_dose = time.time()
        self.icon = icon
        self._enabled = enabled
        self.alarm = False
        self.title = f"Channel {display_channel}" if title is None else title

        self.sensor.set_wet_point(wet_point)
        self.sensor.set_dry_point(dry_point)
示例#4
0
def test_moisture_read(GPIO, smbus):
    from grow.moisture import Moisture

    assert Moisture(channel=1).saturation == 1.0
    assert Moisture(channel=2).saturation == 1.0
    assert Moisture(channel=3).saturation == 1.0

    assert Moisture(channel=1).moisture == 0
    assert Moisture(channel=2).moisture == 0
    assert Moisture(channel=3).moisture == 0
示例#5
0
class Channel:
    colors = [COLOR_BLUE, COLOR_GREEN, COLOR_YELLOW, COLOR_RED]

    def __init__(
        self,
        display_channel,
        sensor_channel,
        pump_channel,
        title=None,
        water_level=0.5,
        warn_level=0.5,
        pump_speed=0.5,
        pump_time=0.2,
        watering_delay=60,
        wet_point=0.7,
        dry_point=26.7,
        icon=None,
        auto_water=False,
        enabled=False,
    ):
        self.channel = display_channel
        self.sensor = Moisture(sensor_channel)
        self.pump = Pump(pump_channel)
        self.water_level = water_level
        self.warn_level = warn_level
        self.auto_water = auto_water
        self.pump_speed = pump_speed
        self.pump_time = pump_time
        self.watering_delay = watering_delay
        self._wet_point = wet_point
        self._dry_point = dry_point
        self.last_dose = time.time()
        self.icon = icon
        self._enabled = enabled
        self.alarm = False
        self.title = f"Channel {display_channel}" if title is None else title

        self.sensor.set_wet_point(wet_point)
        self.sensor.set_dry_point(dry_point)

    @property
    def enabled(self):
        return self._enabled

    @enabled.setter
    def enabled(self, enabled):
        self._enabled = enabled

    @property
    def wet_point(self):
        return self._wet_point

    @property
    def dry_point(self):
        return self._dry_point

    @wet_point.setter
    def wet_point(self, wet_point):
        self._wet_point = wet_point
        self.sensor.set_wet_point(wet_point)

    @dry_point.setter
    def dry_point(self, dry_point):
        self._dry_point = dry_point
        self.sensor.set_dry_point(dry_point)

    def warn_color(self):
        value = self.sensor.moisture

    def indicator_color(self, value):
        value = 1.0 - value

        if value == 1.0:
            return self.colors[-1]
        if value == 0.0:
            return self.colors[0]

        value *= len(self.colors) - 1
        a = int(math.floor(value))
        b = a + 1
        blend = float(value - a)

        r, g, b = [
            int(((self.colors[b][i] - self.colors[a][i]) * blend) +
                self.colors[a][i]) for i in range(3)
        ]

        return (r, g, b)

    def update_from_yml(self, config):
        if config is not None:
            self.pump_speed = config.get("pump_speed", self.pump_speed)
            self.pump_time = config.get("pump_time", self.pump_time)
            self.warn_level = config.get("warn_level", self.warn_level)
            self.water_level = config.get("water_level", self.water_level)
            self.watering_delay = config.get("watering_delay",
                                             self.watering_delay)
            self.auto_water = config.get("auto_water", self.auto_water)
            self.enabled = config.get("enabled", self.enabled)
            self.wet_point = config.get("wet_point", self.wet_point)
            self.dry_point = config.get("dry_point", self.dry_point)

        pass

    def __str__(self):
        return """Channel: {channel}
Enabled: {enabled}
Alarm level: {warn_level}
Auto water: {auto_water}
Water level: {water_level}
Pump speed: {pump_speed}
Pump time: {pump_time}
Delay: {watering_delay}
Wet point: {wet_point}
Dry point: {dry_point}
""".format(
            channel=self.channel,
            enabled=self.enabled,
            warn_level=self.warn_level,
            auto_water=self.auto_water,
            water_level=self.water_level,
            pump_speed=self.pump_speed,
            pump_time=self.pump_time,
            watering_delay=self.watering_delay,
            wet_point=self.wet_point,
            dry_point=self.dry_point,
        )

    def water(self):
        if not self.auto_water:
            return False
        if time.time() - self.last_dose > self.watering_delay:
            self.pump.dose(self.pump_speed, self.pump_time, blocking=False)
            self.last_dose = time.time()
            return True
        return False

    def render(self, image, font):
        pass

    def update(self):
        if not self.enabled:
            return
        sat = self.sensor.saturation
        if sat < self.water_level:
            if self.water():
                logging.info(
                    "Watering Channel: {} - rate {:.2f} for {:.2f}sec".format(
                        self.channel, self.pump_speed, self.pump_time))
        if sat < self.warn_level:
            if not self.alarm:
                logging.warning(
                    "Alarm on Channel: {} - saturation is {:.2f}% (warn level {:.2f}%)"
                    .format(self.channel, sat * 100, self.warn_level * 100))
            self.alarm = True
        else:
            self.alarm = False
class Channel:
    bar_colours = [
        (192, 225, 254),  # Blue
        (196, 255, 209),  # Green
        (255, 243, 192),  # Yellow
        (254, 192, 192),  # Red
    ]

    label_colours = [
        (32, 137, 251),  # Blue
        (100, 255, 124),  # Green
        (254, 219, 82),  # Yellow
        (254, 82, 82),  # Red
    ]

    def __init__(
        self,
        display_channel,
        sensor_channel,
        pump_channel,
        water_level=0.5,
        alarm_level=0.5,
        pump_speed=0.7,
        pump_time=0.7,
        watering_delay=30,
        wet_point=0.7,
        dry_point=26.7,
        icon=None,
        auto_water=False,
        enabled=False,
    ):
        self.channel = display_channel
        self.sensor = Moisture(sensor_channel)
        self.pump = Pump(pump_channel)
        self.water_level = water_level
        self.alarm_level = alarm_level
        self.auto_water = auto_water
        self.pump_speed = pump_speed
        self.pump_time = pump_time
        self.watering_delay = watering_delay
        self.wet_point = wet_point
        self.dry_point = dry_point
        self.last_dose = time.time()
        self.icon = icon
        self.enabled = enabled
        self.alarm = False

        self.sensor.set_wet_point(wet_point)
        self.sensor.set_dry_point(dry_point)

    def indicator_color(self, value, r=None):
        if r is None:
            r = self.bar_colours
        if value == 1.0:
            return r[-1]
        if value == 0.0:
            return r[0]

        value *= len(r) - 1
        a = int(math.floor(value))
        b = a + 1
        blend = float(value - a)

        r, g, b = [int(((r[b][i] - r[a][i]) * blend) + r[a][i]) for i in range(3)]

        return (r, g, b)

    def update_from_yml(self, config):
        if config is not None:
            self.pump_speed = config.get("pump_speed", self.pump_speed)
            self.pump_time = config.get("pump_time", self.pump_time)
            self.alarm_level = config.get("alarm_level", self.alarm_level)
            self.water_level = config.get("water_level", self.water_level)
            self.watering_delay = config.get("watering_delay", self.watering_delay)
            self.auto_water = config.get("auto_water", self.auto_water)
            self.enabled = config.get("enabled", self.enabled)
            self.wet_point = config.get("wet_point", self.wet_point)
            self.dry_point = config.get("dry_point", self.dry_point)
            icon = config.get("icon", None)
            if icon is not None:
                self.icon = Image.open(icon)

        pass

    def __str__(self):
        return """Channel: {channel}
Enabled: {enabled}
Alarm level: {alarm_level}
Auto water: {auto_water}
Water level: {water_level}
Pump speed: {pump_speed}
Pump time: {pump_time}
Delay: {watering_delay}
Wet point: {wet_point}
Dry point: {dry_point}
""".format(
            **self.__dict__
        )

    def water(self):
        if not self.auto_water:
            return False
        if time.time() - self.last_dose > self.watering_delay:
            self.pump.dose(self.pump_speed, self.pump_time, blocking=False)
            self.last_dose = time.time()
            return True
        return False

    def render(self, image, font, selected=False):
        draw = ImageDraw.Draw(image)
        x = [21, 61, 101][self.channel - 1]

        # Saturation amounts from each sensor
        c = 1.0 - self.sensor.saturation
        active = self.sensor.active and self.enabled

        if active:
            # Draw background bars
            draw.rectangle(
                (x, int(c * HEIGHT), x + 37, HEIGHT),
                self.indicator_color(c) if active else (229, 229, 229),
            )

        # Draw plant image
        x -= 3
        y = HEIGHT - self.icon.height
        pl = self.icon
        if not active:
            pl = pl.convert("LA").convert("RGB")
        image.paste(pl, (x, y), mask=self.icon)

        # Channel selection icons
        x += 15
        draw.rectangle(
            (x, 2, x + 15, 17),
            self.indicator_color(c, self.label_colours) if active else (129, 129, 129),
        )

        if selected:
            selected_x = x - 2
            draw.rectangle(
                (selected_x, 0, selected_x + 19, 20),
                self.indicator_color(c, self.label_colours)
                if active
                else (129, 129, 129),
            )

            # TODO: replace with graphic, since PIL has no anti-aliasing
            draw.polygon(
                [(selected_x, 20), (selected_x + 9, 25), (selected_x + 19, 20)],
                fill=self.indicator_color(c, self.label_colours)
                if active
                else (129, 129, 129),
            )

        # TODO: replace number text with graphic

        tw, th = font.getsize("{}".format(self.channel))
        draw.text(
            (x + int(math.ceil(8 - (tw / 2.0))), 2),
            "{}".format(self.channel),
            font=font,
            fill=(255, 255, 255),
        )

    def update(self):
        if not self.enabled:
            return
        sat = self.sensor.saturation
        if sat < self.water_level:
            if self.water():
                logging.info(
                    "Watering Channel: {} - rate {:.2f} for {:.2f}sec".format(
                        self.channel, self.pump_speed, self.pump_time
                    )
                )
            if sat < self.alarm_level and not self.alarm:
                logging.warning(
                    "Alarm on Channel: {} - saturation is {:.2f} (warn level {:.2f})".format(
                        self.channel, sat, self.alarm_level
                    )
                )
                self.alarm = True
示例#7
0
# Default watering settings
dry_level = 0.7  # Saturation level considered dry
dose_speed = 0.63  # Pump speed for water dose
dose_time = 0.96  # Time (in seconds) for water dose

# Here be dragons!
FPS = 15  # Display framerate
NUM_SAMPLES = 10  # Number of saturation level samples to average over
DOSE_FREQUENCY = 30.0  # Minimum time between automatic waterings (in seconds)

BUTTONS = [5, 6, 16, 24]
LABELS = ["A", "B", "X", "Y"]

p = Pump(pump_channel)
m = Moisture(moisture_channel)

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP)

mode = 0
last_dose = time.time()
saturation = [1.0 for _ in range(NUM_SAMPLES)]

display = ST7735.ST7735(port=0,
                        cs=1,
                        dc=9,
                        backlight=12,
                        rotation=270,
                        spi_speed_hz=80000000)
示例#8
0
import time

from grow.moisture import Moisture

print("""moisture.py - Print out sensor reading in Hz

Press Ctrl+C to exit!

""")

m1 = Moisture(1)
m2 = Moisture(2)
m3 = Moisture(3)

while True:
    print(f"""1: {m1.moisture}
2: {m2.moisture}
3: {m3.moisture}
""")
    time.sleep(1.0)
示例#9
0
def main():
    args = _get_args()
    # Setup Logging
    numeric_log_level = getattr(logging, args.log.upper(), None)
    if not isinstance(numeric_log_level, int):
        raise ValueError("Invalid log level: %s" % args.log)
    logging.basicConfig(
        level=numeric_log_level,
        format="[%(levelname)s] %(message)s",
        filename=args.log_file,
    )

    # Start
    logging.info("Starting up...")

    # Import settings
    config = yaml.safe_load(open(args.config_path))

    # Setup moisture sensors for reading
    sensors = []
    for sensor_num in [1, 2, 3]:
        sensor = Moisture(sensor_num)
        if ("channel{0}".format(sensor_num)) in config.keys():
            sensors.append(sensor)

    # Setup light sensor for reading
    light = LTR559()

    soil_moisture = Gauge("grow_soil_moisture", "Soil Moisture",
                          ["channel", "plant"])
    soil_saturation = Gauge("grow_soil_saturation", "Soil Saturation",
                            ["channel", "plant"])
    soil_dry_point = Gauge("grow_soil_dry_point", "Dry Point",
                           ["channel", "plant"])
    soil_wet_point = Gauge("grow_soil_wet_point", "Dry Point",
                           ["channel", "plant"])
    lux = Gauge("grow_light_lux", "Lux")

    # Start prometheus server
    start_http_server(args.bind_port)

    # Gather/export data
    while True:
        # Re-read settings if changed by monitor app
        config = yaml.safe_load(open(args.config_path))

        for i in range(0, 3):
            channel_num = i + 1
            channel_name = "channel{}".format(channel_num)
            if channel_name in config.keys():
                plant_name = config[channel_name]["name"]

                # Set wet/dry points
                logging.debug("Setting wet/dry points.")
                sensors[i].set_dry_point(config[channel_name]["dry_point"])
                sensors[i].set_wet_point(config[channel_name]["wet_point"])

                # Track soil moisture/saturation
                logging.debug("Tracking soil moisture/saturation.")
                soil_moisture.labels(channel_name,
                                     plant_name).set(sensors[i].moisture)
                soil_saturation.labels(channel_name,
                                       plant_name).set(sensors[i].saturation)

                # Track wet/dry points
                logging.debug("Tracking wet/dry points.")
                soil_dry_point.labels(channel_name, plant_name).set(
                    config[channel_name]["dry_point"])
                soil_wet_point.labels(channel_name, plant_name).set(
                    config[channel_name]["wet_point"])

        # Update/track light sensor lux
        logging.debug("Updating light sensor.")
        light.update_sensor()

        logging.debug("Tracking light lux.")
        lux.set(light.get_lux())

        logging.debug("Sleeping for {}".format(args.interval))
        time.sleep(args.interval)

    # End
    logging.info("Shutting down ...")
示例#10
0
 def __init__(self, channel, wet_point=None, dry_point=None):
     self._sensor = Moisture(channel,
                             wet_point=wet_point,
                             dry_point=dry_point)
     self.topic = "grow/soil/{}".format(channel)