Tweaks, fixes, linting and docs

This commit is contained in:
Phil Howard
2020-08-27 12:26:08 +01:00
parent de90e2289c
commit 3123e27939
14 changed files with 229 additions and 217 deletions

View File

@@ -27,7 +27,7 @@ check:
@echo "Checking for trailing whitespace"
@! grep -IUrn --color "[[:blank:]]$$" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO
@echo "Checking for DOS line-endings"
@! grep -IUrn --color "
@! grep -lIUrn --color "
" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile
@echo "Checking library/CHANGELOG.txt"
@cat library/CHANGELOG.txt | grep ^${LIBRARY_VERSION}
@@ -36,14 +36,14 @@ check:
tag:
git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}"
python-readme: library/README.md
python-license: library/LICENSE.txt
library/README.rst: README.md library/CHANGELOG.txt
pandoc --from=markdown --to=rst -o library/README.rst README.md
echo "" >> library/README.rst
library/README.md: README.md library/CHANGELOG.txt
cp README.md library/README.md
printf "\n# Changelog\n" >> library/README.md
cat library/CHANGELOG.txt >> library/README.md
library/LICENSE.txt: LICENSE

View File

@@ -29,21 +29,22 @@ curl -sSL https://get.pimoroni.com/grow | bash
## Or... Install from PyPi and configure manually:
* Run `sudo pip install growhat`
* Install dependencies:
```
sudo apt install python3-setuptools python3-pip python3-yaml python3-smbus python3-pil python3-spidev python3-rpi.gpio
```
* Run `sudo pip3 install growhat`
**Note** this wont perform any of the required configuration changes on your Pi, you may additionally need to:
* Enable i2c: `raspi-config nonint do_i2c 0`
* Enable SPI: `raspi-config nonint do_spi 0`
And install additional dependencies:
```
sudo apt install python-numpy python-smbus python-pil python-setuptools
```
* Enable i2c: `sudo raspi-config nonint do_i2c 0`
* Enable SPI: `sudo raspi-config nonint do_spi 0`
* Add the following to `/boot/config.txt`: `dtoverlay=spi0-cs,cs0_pin=14`
## Help & Support
* GPIO Pinout - https://pinout.xyz/pinout/enviro_plus
* GPIO Pinout - https://pinout.xyz/pinout/grow_hat_mini
* Support forums - http://forums.pimoroni.com/c/support
* Discord - https://discord.gg/hr93ByC

View File

@@ -1,7 +1,5 @@
# Grow <!-- omit in toc -->
Grow
- [Getting Started](#getting-started)
- [Requirements](#requirements)
- [Python 3 & pip](#python-3--pip)
@@ -38,7 +36,7 @@ You should use Python 3, which may need installing on your Pi:
```
sudo apt update
sudo apt install python3 python3-pip
sudo apt install python3 python3-pip python3-setuptools
```
#### Enabling i2c and spi
@@ -50,6 +48,14 @@ sudo raspi-config nonint do_i2c 0
sudo raspi-config nonint do_spi 0
```
### Installing Dependencies
The following dependencies are required:
```
sudo apt install python3-yaml python3-smbus python3-pil python3-spidev python3-rpi.gpio
```
### Installing the library
```python
@@ -174,7 +180,7 @@ while True:
moisture1.active()
```
Returns `True` if the moisture sensor is connected and returning valid readings.
Returns `True` if the moisture sensor is connected and returning valid readings.
Checks if a pulse has happened within the last second, and that the reading is within a sensible range.

View File

@@ -1,8 +1,35 @@
# Watering Settings
# Monitoring and/or Watering Your Plants
The `grow-monitor-and-water.py` example can monitor and, optionally, automatically water all three Grow channels.
It's configured using a settings file - `water.yml` - that looks like the following:
By default auto-watering is disabled and an alarm will sound every 1s if the `warn_level` is reached.
Run it with `python3 grow-monitor-and-water.py`.
## Monitoring
Grow can monitor the moisture level of your soil, sounding an alarm when it dries out.
Grow is configured using `settings.yml`. Your settings for monitoring only will look something like this:
```yaml
channel1:
warn_level: 0.2
icon: icons/flat-4.png
channel2:
warn_level: 0.2
channel3:
warn_level: 0.2
general:
alarm_enable: True
alarm_interval: 1.0
```
## Watering
If you've got pumps attached to Grow and want to automatically water your plants, you'll need some extra configuration options.
See [Channel Settings](#channel-settings) and [General Settings](#general-settings) for more information on what these do.
```yaml
channel1:
@@ -29,10 +56,10 @@ general:
alarm_interval: 1.0
```
By default auto-watering is disabled and an alarm will sound every 1s if the `warn_level` is reached.
## Channel Settings
Grow has three channels which are separated into the sections `channel1`, `channel2` and `channel3`, each of these sections has the following configuration options:
* `water_level` - The level at which auto-watering should be triggered (soil saturation from 0.0 to 1.0)
* `warn_level` - The level at which the alarm should be triggered (soil saturation from 0.0 to 1.0)
* `pump_speed` - The speed at which the pump should be run (from 0.0 low speed to 1.0 full speed)
@@ -42,5 +69,7 @@ By default auto-watering is disabled and an alarm will sound every 1s if the `wa
## General Settings
An additional `general` section can be used for global settings:
* `alarm_enable` - Whether to enable the alarm
* `alarm_interval` - The interval at which the alarm should beep (in seconds)

View File

@@ -6,24 +6,22 @@ from fonts.ttf import RobotoMedium as UserFont
import logging
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
format="%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s",
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
datefmt="%Y-%m-%d %H:%M:%S",
)
logging.info("""lcd.py - Hello, World! example on the 0.96" LCD.
logging.info(
"""lcd.py - Hello, World! example on the 0.96" LCD.
Press Ctrl+C to exit!
""")
"""
)
# Create LCD class instance.
disp = ST7735.ST7735(
port=0,
cs=1,
dc=9,
backlight=12,
rotation=270,
spi_speed_hz=10000000
port=0, cs=1, dc=9, backlight=12, rotation=270, spi_speed_hz=10000000
)
# Initialize display.
@@ -34,7 +32,7 @@ WIDTH = disp.width
HEIGHT = disp.height
# New canvas to draw on.
img = Image.new('RGB', (WIDTH, HEIGHT), color=(0, 0, 0))
img = Image.new("RGB", (WIDTH, HEIGHT), color=(0, 0, 0))
draw = ImageDraw.Draw(img)
# Text settings.

View File

@@ -20,17 +20,29 @@ class Channel:
(192, 225, 254), # Blue
(196, 255, 209), # Green
(255, 243, 192), # Yellow
(254, 192, 192) # Red
(254, 192, 192), # Red
]
label_colours = [
(32, 137, 251), # Blue
(32, 137, 251), # Blue
(100, 255, 124), # Green
(254, 219, 82), # Yellow
(254, 82, 82), # Red
(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, icon=None, auto_water=False):
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,
icon=None,
auto_water=False,
):
self.channel = display_channel
self.sensor = Moisture(sensor_channel)
self.pump = Pump(pump_channel)
@@ -77,13 +89,15 @@ class Channel:
def __str__(self):
return """Channel: {channel}
Water level: {water_level}
Alarm level: {alarm_level}
Auto water: {auto_water}
Water level: {water_level}
Pump speed: {pump_speed}
Pump time: {pump_time}
Delay: {watering_delay}
""".format(**self.__dict__)
""".format(
**self.__dict__
)
def water(self):
if not self.auto_water:
@@ -103,7 +117,10 @@ Delay: {watering_delay}
active = self.sensor.active
# Draw background bars
draw.rectangle((x, int(c * HEIGHT), x + 37, HEIGHT), self.indicator_color(c) if active else (229, 229, 229))
draw.rectangle(
(x, int(c * HEIGHT), x + 37, HEIGHT),
self.indicator_color(c) if active else (229, 229, 229),
)
# Draw plant image
x -= 3
@@ -115,37 +132,58 @@ Delay: {watering_delay}
# 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))
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))
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))
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))
draw.text(
(x + int(math.ceil(8 - (tw / 2.0))), 2),
"{}".format(self.channel),
font=font,
fill=(255, 255, 255),
)
def update(self):
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))
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))
logging.warning(
"Alarm on Channel: {} - saturation is {:.2f} (warn level {:.2f})".format(
self.channel, sat, self.alarm_level
)
)
self.alarm = True
BUTTONS = [5, 6, 16, 24]
LABELS = ['A', 'B', 'X', 'Y']
LABELS = ["A", "B", "X", "Y"]
channel_selected = 0
alarm = False
@@ -158,15 +196,16 @@ for x in range(1, 15):
# Pick a random selection of plant icons to display on screen
channels = [
Channel(1, 1, 1, icon=random.choice(plants)),
Channel(2, 2, 2, icon=random.choice(plants)),
Channel(3, 3, 3, icon=random.choice(plants))
Channel(1, 1, 1, icon=random.choice(plants)),
Channel(2, 2, 2, icon=random.choice(plants)),
Channel(3, 3, 3, icon=random.choice(plants)),
]
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
format="%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s",
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
datefmt="%Y-%m-%d %H:%M:%S",
)
def handle_button(pin):
@@ -174,19 +213,19 @@ def handle_button(pin):
index = BUTTONS.index(pin)
label = LABELS[index]
if label == 'A': # Select Channel
if label == "A": # Select Channel
channel_selected += 1
channel_selected %= len(channels)
if label == 'B': # Cancel Alarm
if label == "B": # Cancel Alarm
alarm = False
for channel in channels:
channel.alarm = False
if label == 'X': # Set Wet Point
if label == "X": # Set Wet Point
channels[channel_selected].sensor.set_wet_point()
if label == 'Y': # Set Dry Point
if label == "Y": # Set Dry Point
channels[channel_selected].sensor.set_dry_point()
@@ -209,12 +248,7 @@ CHANNEL_M = 2
# Set up the ST7735 SPI Display
display = ST7735.ST7735(
port=0,
cs=1,
dc=9,
backlight=12,
rotation=270,
spi_speed_hz=80000000
port=0, cs=1, dc=9, backlight=12, rotation=270, spi_speed_hz=80000000
)
display.begin()
WIDTH, HEIGHT = display.width, display.height
@@ -256,7 +290,7 @@ def render():
draw.rectangle((21, 0, 138, HEIGHT), (255, 255, 255)) # Erase channel area
for channel in channels:
channel.render(image, font, channel_selected==channel.channel - 1)
channel.render(image, font, channel_selected == channel.channel - 1)
# Draw the snooze icon- will be pulsing red if the alarm state is True
r = 129
@@ -271,7 +305,7 @@ def main():
piezo = Piezo()
time_last_beep = time.time()
settings_file = "water.yml"
settings_file = "settings.yml"
if len(sys.argv) > 1:
settings_file = sys.argv[1]
settings_file = pathlib.Path(settings_file)
@@ -279,7 +313,9 @@ def main():
try:
config = yaml.safe_load(open(settings_file))
except yaml.parser.ParserError as e:
raise yaml.parser.ParserError("Error parsing settings file: {} ({})".format(settings_file, e))
raise yaml.parser.ParserError(
"Error parsing settings file: {} ({})".format(settings_file, e)
)
for channel in channels:
ch = config.get("channel{}".format(channel.channel), None)
@@ -294,13 +330,14 @@ def main():
for channel in channels:
print(channel)
print("""Settings:
print(
"""Settings:
Alarm Enabled: {}
Alarm Interval: {:.2f}s
""".format(
alarm_enable,
alarm_interval
))
alarm_enable, alarm_interval
)
)
while True:
update()

10
examples/settings.yml Normal file
View File

@@ -0,0 +1,10 @@
channel1:
warn_level: 0.2
icon: icons/flat-4.png
channel2:
warn_level: 0.2
channel3:
warn_level: 0.2
general:
alarm_enable: True
alarm_interval: 1.0

View File

@@ -29,17 +29,17 @@ pump_channel = 3
moisture_channel = 3
# 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
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
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']
LABELS = ["A", "B", "X", "Y"]
p = Pump(pump_channel)
m = Moisture(moisture_channel)
@@ -53,12 +53,7 @@ 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
port=0, cs=1, dc=9, backlight=12, rotation=270, spi_speed_hz=80000000
)
display.begin()
@@ -68,25 +63,26 @@ image = Image.new("RGBA", (display.width, display.height), color=(0, 0, 0))
draw = ImageDraw.Draw(image)
logging.basicConfig(
format='%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s',
format="%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s",
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
datefmt="%Y-%m-%d %H:%M:%S",
)
def handle_button(pin):
global mode, last_dose, dose_time, dose_speed, dry_level
index = BUTTONS.index(pin)
label = LABELS[index]
if label == 'A': # Test
if label == "A": # Test
logging.info("Manual watering triggered.")
p.dose(dose_speed, dose_time, blocking=False)
last_dose = time.time()
if label == 'B': # Switch setting
if label == "B": # Switch setting
mode += 1
mode %= 3 # Wrap 0, 1, 2 (Time, Speed, Dry level)
mode %= 3 # Wrap 0, 1, 2 (Time, Speed, Dry level)
if label == 'Y': # Inc. setting
if label == "Y": # Inc. setting
if mode == 0:
dose_time += 0.01
logging.info("Dose time increased to: {:.2f}".format(dose_time))
@@ -97,7 +93,7 @@ def handle_button(pin):
dry_level += 0.01
logging.info("Dry level increased to: {:.2f}".format(dry_level))
if label == 'X': # Dec. setting
if label == "X": # Dec. setting
if mode == 0:
dose_time -= 0.01
logging.info("Dose time decreased to: {:.2f}".format(dose_time))
@@ -132,36 +128,76 @@ try:
# has had the opportunity to catch up.
if avg_saturation < dry_level and (time.time() - last_dose) > DOSE_FREQUENCY:
p.dose(dose_speed, dose_time)
logging.info("Auto watering. Saturation: {:.2f} (Dry: {:.2f})".format(avg_saturation, dry_level))
logging.info(
"Auto watering. Saturation: {:.2f} (Dry: {:.2f})".format(
avg_saturation, dry_level
)
)
last_dose = time.time()
draw.rectangle((0, 0, display.width, display.height), (0, 0, 0))
# Current and average saturation
draw.text((5 + display.width // 2, 16), "Sat: {:.3f}".format(current_saturation), font=font, fill=(255, 255, 255))
draw.text((5 + display.width // 2, 32), "AVG: {:.3f}".format(avg_saturation), font=font, fill=(255, 255, 255))
draw.text(
(5 + display.width // 2, 16),
"Sat: {:.3f}".format(current_saturation),
font=font,
fill=(255, 255, 255),
)
draw.text(
(5 + display.width // 2, 32),
"AVG: {:.3f}".format(avg_saturation),
font=font,
fill=(255, 255, 255),
)
# Selected setting box
draw.rectangle((0, 16 + (16 * mode), display.width // 2, 31 + (16 * mode)), (30, 30, 30))
draw.rectangle(
(0, 16 + (16 * mode), display.width // 2, 31 + (16 * mode)), (30, 30, 30)
)
draw.text((5, 16), "Time: {:.2f}".format(dose_time), font=font, fill=(255, 255, 255) if mode == 0 else (128, 128, 128))
draw.text((5, 32), "Speed: {:.2f}".format(dose_speed), font=font, fill=(255, 255, 255) if mode == 1 else (128, 128, 128))
draw.text((5, 48), "Dry lvl: {:.2f}".format(dry_level), font=font, fill=(255, 255, 255) if mode == 2 else (128, 128, 128))
draw.text(
(5, 16),
"Time: {:.2f}".format(dose_time),
font=font,
fill=(255, 255, 255) if mode == 0 else (128, 128, 128),
)
draw.text(
(5, 32),
"Speed: {:.2f}".format(dose_speed),
font=font,
fill=(255, 255, 255) if mode == 1 else (128, 128, 128),
)
draw.text(
(5, 48),
"Dry lvl: {:.2f}".format(dry_level),
font=font,
fill=(255, 255, 255) if mode == 2 else (128, 128, 128),
)
# Button lavel backgrounds
draw.rectangle((0, 0, 42, 14), (255, 255, 255))
draw.rectangle((display.width - 15, 0, display.width, 14), (255, 255, 255))
draw.rectangle((display.width - 15, display.height - 14, display.width, display.height), (255, 255, 255))
draw.rectangle(
(display.width - 15, display.height - 14, display.width, display.height),
(255, 255, 255),
)
draw.rectangle((0, display.height - 14, 42, display.height), (255, 255, 255))
# Button labels
draw.text((5, 0), "test", font=font, fill=(0, 0, 0))
draw.text((5, display.height - 16), "next", font=font, fill=(0, 0, 0))
draw.text((display.width - 10, 0), "-", font=font, fill=(0, 0, 0))
draw.text((display.width - 12, display.height - 15), "+", font=font, fill=(0, 0, 0))
draw.text(
(display.width - 12, display.height - 15), "+", font=font, fill=(0, 0, 0)
)
display.display(image)
time.sleep(1.0 / FPS)
except KeyboardInterrupt:
print("Dose Time: {:.2f} Dose Speed: {:.2f}, Dry Level: {:.2f}".format(dose_time, dose_speed, dry_level))
print(
"Dose Time: {:.2f} Dose Speed: {:.2f}, Dry Level: {:.2f}".format(
dose_time, dose_speed, dry_level
)
)

View File

@@ -1,22 +0,0 @@
channel1:
water_level: 0.8
warn_level: 0.2
pump_speed: 0.7
pump_time: 0.7
auto_water: False
icon: icons/flat-4.png
channel2:
water_level: 0.8
warn_level: 0.2
pump_speed: 0.7
pump_time: 0.7
auto_water: False
channel3:
water_level: 0.8
warn_level: 0.2
pump_speed: 0.7
pump_time: 0.7
auto_water: False
general:
alarm_enable: True
alarm_interval: 1.0

View File

@@ -1,70 +0,0 @@
Grow
=======
Designed for looking after plants, Grow monitors moisture levels and runs simple pumps to water plants. Learn more -
https://shop.pimoroni.com/products/grow
|Build Status| |Coverage Status| |PyPi Package| |Python Versions|
Installing
==========
The one-line installer enables the correct interfaces,
One-line (Installs from GitHub)
-------------------------------
::
curl -sSL https://get.pimoroni.com/grow | bash
**Note** report issues with one-line installer here:
https://github.com/pimoroni/get
Or... Install and configure dependencies from GitHub:
-----------------------------------------------------
- ``git clone https://github.com/pimoroni/grow-python``
- ``cd grow-python``
- ``sudo ./install.sh``
**Note** Raspbian Lite users may first need to install git:
``sudo apt install git``
Or... Install from PyPi and configure manually:
-----------------------------------------------
- Run ``sudo pip install grow``
**Note** this wont perform any of the required configuration changes on
your Pi, you may additionally need to:
- Enable i2c: ``raspi-config nonint do_i2c 0``
- Enable SPI: ``raspi-config nonint do_spi 0``
And install additional dependencies:
::
sudo apt install python-numpy python-smbus python-pil python-setuptools
Help & Support
--------------
- GPIO Pinout - https://pinout.xyz/pinout/grow
- Support forums - http://forums.pimoroni.com/c/support
- Discord - https://discord.gg/hr93ByC
.. |Build Status| image:: https://travis-ci.com/pimoroni/grow-python.svg?branch=master
:target: https://travis-ci.com/pimoroni/grow-python
.. |Coverage Status| image:: https://coveralls.io/repos/github/pimoroni/grow-python/badge.svg?branch=master
:target: https://coveralls.io/github/pimoroni/grow-python?branch=master
.. |PyPi Package| image:: https://img.shields.io/pypi/v/growhat.svg
:target: https://pypi.python.org/pypi/growhat
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/growhat.svg
:target: https://pypi.python.org/pypi/growhat
0.0.1
-----
* Initial Release

View File

@@ -5,7 +5,8 @@ version = 0.0.1
author = Philip Howard, Paul Beech
author_email = paul@pimoroni.com
description = Grow HAT Mini. A plant valet add-on for the Raspberry Pi
long_description = file: README.rst
long_description = file: README.md
long_description_content_type = text/markdown
keywords = Raspberry Pi
url = https://www.pimoroni.com
project_urls =
@@ -48,14 +49,14 @@ ignore =
[pimoroni]
py2deps =
python-pip
python-numpy
python-yaml
python-smbus
python-pil
python-spidev
python-rpi.gpio
py3deps =
python3-pip
python3-numpy
python3-yaml
python3-smbus
python3-pil
python3-spidev

View File

@@ -22,12 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from setuptools import setup, __version__
from pkg_resources import parse_version
from setuptools import setup
minimum_version = parse_version('30.4.0')
if parse_version(__version__) < minimum_version:
raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version))
setup()

View File

@@ -70,12 +70,3 @@ def atexit():
sys.modules['atexit'] = atexit
yield atexit
del sys.modules['atexit']
@pytest.fixture(scope='function', autouse=False)
def numpy():
"""Mock numpy module."""
numpy = mock.MagicMock()
sys.modules['numpy'] = numpy
yield numpy
del sys.modules['numpy']

View File

@@ -9,7 +9,7 @@ def test_moisture_setup(GPIO, smbus):
def test_moisture_read_all(GPIO, smbus):
from grow import moisture
moisture._is_setup = False
result = moisture.read_all()
assert type(result(1)) == float