mirror of
https://github.com/pimoroni/grow-python
synced 2025-10-25 15:19:23 +00:00
Compare commits
6 Commits
c36bc2ac93
...
9eda924814
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eda924814 | ||
|
|
bc22e3fdc6 | ||
|
|
7513fde92a | ||
|
|
2467467f15 | ||
|
|
b04b7445cf | ||
|
|
70eb9b3c02 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -19,10 +19,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
make build
|
||||
|
||||
- name: Upload Packages
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.RELEASE_FILE }}
|
||||
path: dist/
|
||||
|
||||
5
.github/workflows/qa.yml
vendored
5
.github/workflows/qa.yml
vendored
@@ -10,16 +10,15 @@ jobs:
|
||||
test:
|
||||
name: linting & spelling
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
TERM: xterm-256color
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python '3,11'
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
|
||||
@@ -4,18 +4,28 @@ import atexit
|
||||
import threading
|
||||
import time
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
import gpiodevice
|
||||
|
||||
from . import pwm
|
||||
|
||||
PLATFORMS = {
|
||||
"Raspberry Pi 5": {"piezo": ("PIN33", pwm.OUTL)},
|
||||
"Raspberry Pi 4": {"piezo": ("GPIO13", pwm.OUTL)},
|
||||
}
|
||||
|
||||
|
||||
class Piezo():
|
||||
def __init__(self, gpio_pin=13):
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(gpio_pin, GPIO.OUT, initial=GPIO.LOW)
|
||||
self.pwm = GPIO.PWM(gpio_pin, 440)
|
||||
self.pwm.start(0)
|
||||
def __init__(self, gpio_pin=None):
|
||||
|
||||
if gpio_pin is None:
|
||||
gpio_pin = gpiodevice.get_pins_for_platform(PLATFORMS)[0]
|
||||
elif isinstance(gpio_pin, str):
|
||||
gpio_pin = gpiodevice.get_pin(gpio_pin, "piezo", pwm.OUTL)
|
||||
|
||||
self.pwm = pwm.PWM(gpio_pin)
|
||||
self._timeout = None
|
||||
atexit.register(self._exit)
|
||||
pwm.PWM.start_thread()
|
||||
atexit.register(pwm.PWM.stop_thread)
|
||||
|
||||
def frequency(self, value):
|
||||
"""Change the piezo frequency.
|
||||
@@ -23,17 +33,15 @@ class Piezo():
|
||||
Loosely corresponds to musical pitch, if you suspend disbelief.
|
||||
|
||||
"""
|
||||
self.pwm.ChangeFrequency(value)
|
||||
self.pwm.set_frequency(value)
|
||||
|
||||
def start(self, frequency=None):
|
||||
def start(self, frequency):
|
||||
"""Start the piezo.
|
||||
|
||||
Sets the Duty Cycle to 100%
|
||||
Sets the Duty Cycle to 50%
|
||||
|
||||
"""
|
||||
if frequency is not None:
|
||||
self.frequency(frequency)
|
||||
self.pwm.ChangeDutyCycle(1)
|
||||
self.pwm.start(frequency=frequency, duty_cycle=0.5)
|
||||
|
||||
def stop(self):
|
||||
"""Stop the piezo.
|
||||
@@ -41,7 +49,7 @@ class Piezo():
|
||||
Sets the Duty Cycle to 0%
|
||||
|
||||
"""
|
||||
self.pwm.ChangeDutyCycle(0)
|
||||
self.pwm.stop()
|
||||
|
||||
def beep(self, frequency=440, timeout=0.1, blocking=True, force=False):
|
||||
"""Beep the piezo for time seconds.
|
||||
@@ -67,6 +75,3 @@ class Piezo():
|
||||
self.start(frequency=frequency)
|
||||
self._timeout.start()
|
||||
return True
|
||||
|
||||
def _exit(self):
|
||||
self.pwm.stop()
|
||||
|
||||
40
grow/pump.py
40
grow/pump.py
@@ -2,13 +2,20 @@ import atexit
|
||||
import threading
|
||||
import time
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
import gpiodevice
|
||||
|
||||
PUMP_1_PIN = 17
|
||||
PUMP_2_PIN = 27
|
||||
PUMP_3_PIN = 22
|
||||
from . import pwm
|
||||
|
||||
PUMP_1_PIN = "PIN11" # 17
|
||||
PUMP_2_PIN = "PIN13" # 27
|
||||
PUMP_3_PIN = "PIN15" # 22
|
||||
PUMP_PWM_FREQ = 10000
|
||||
PUMP_MAX_DUTY = 90
|
||||
PUMP_MAX_DUTY = 0.9
|
||||
|
||||
PLATFORMS = {
|
||||
"Raspberry Pi 5": {"pump1": ("PIN11", pwm.OUTL), "pump2": ("PIN12", pwm.OUTL), "pump3": ("PIN15", pwm.OUTL)},
|
||||
"Raspberry Pi 4": {"pump1": ("GPIO17", pwm.OUTL), "pump2": ("GPIO27", pwm.OUTL), "pump3": ("GPIO22", pwm.OUTL)},
|
||||
}
|
||||
|
||||
|
||||
global_lock = threading.Lock()
|
||||
@@ -17,6 +24,8 @@ global_lock = threading.Lock()
|
||||
class Pump(object):
|
||||
"""Grow pump driver."""
|
||||
|
||||
PINS = None
|
||||
|
||||
def __init__(self, channel=1):
|
||||
"""Create a new pump.
|
||||
|
||||
@@ -26,22 +35,19 @@ class Pump(object):
|
||||
|
||||
"""
|
||||
|
||||
self._gpio_pin = [PUMP_1_PIN, PUMP_2_PIN, PUMP_3_PIN][channel - 1]
|
||||
if Pump.PINS is None:
|
||||
Pump.PINS = gpiodevice.get_pins_for_platform(PLATFORMS)
|
||||
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setup(self._gpio_pin, GPIO.OUT, initial=GPIO.LOW)
|
||||
self._pwm = GPIO.PWM(self._gpio_pin, PUMP_PWM_FREQ)
|
||||
self._gpio_pin = Pump.PINS[channel - 1]
|
||||
|
||||
self._pwm = pwm.PWM(self._gpio_pin, PUMP_PWM_FREQ)
|
||||
self._pwm.start(0)
|
||||
|
||||
pwm.PWM.start_thread()
|
||||
atexit.register(pwm.PWM.stop_thread)
|
||||
|
||||
self._timeout = None
|
||||
|
||||
atexit.register(self._stop)
|
||||
|
||||
def _stop(self):
|
||||
self._pwm.stop(0)
|
||||
GPIO.setup(self._gpio_pin, GPIO.IN)
|
||||
|
||||
def set_speed(self, speed):
|
||||
"""Set pump speed (PWM duty cycle)."""
|
||||
if speed > 1.0 or speed < 0:
|
||||
@@ -52,7 +58,7 @@ class Pump(object):
|
||||
elif not global_lock.acquire(blocking=False):
|
||||
return False
|
||||
|
||||
self._pwm.ChangeDutyCycle(int(PUMP_MAX_DUTY * speed))
|
||||
self._pwm.set_duty_cycle(PUMP_MAX_DUTY * speed)
|
||||
self._speed = speed
|
||||
return True
|
||||
|
||||
|
||||
106
grow/pwm.py
Normal file
106
grow/pwm.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
import gpiod
|
||||
import gpiodevice
|
||||
from gpiod.line import Direction, Value
|
||||
|
||||
OUTL = gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE)
|
||||
|
||||
|
||||
class PWM:
|
||||
_pwms: list = []
|
||||
_t_pwm: Thread = None
|
||||
_pwm_running: bool = False
|
||||
|
||||
@staticmethod
|
||||
def start_thread():
|
||||
if PWM._t_pwm is None:
|
||||
PWM._pwm_running = True
|
||||
PWM._t_pwm = Thread(target=PWM._run)
|
||||
PWM._t_pwm.start()
|
||||
|
||||
@staticmethod
|
||||
def stop_thread():
|
||||
if PWM._t_pwm is not None:
|
||||
PWM._pwm_running = False
|
||||
PWM._t_pwm.join()
|
||||
PWM._t_pwm = None
|
||||
|
||||
@staticmethod
|
||||
def _add(pwm):
|
||||
PWM._pwms.append(pwm)
|
||||
|
||||
@staticmethod
|
||||
def _remove(pwm):
|
||||
index = PWM._pwms.index(pwm)
|
||||
del PWM._pwms[index]
|
||||
if len(PWM._pwms) == 0:
|
||||
PWM.stop_thread()
|
||||
|
||||
@staticmethod
|
||||
def _run():
|
||||
while PWM._pwm_running:
|
||||
PWM.run()
|
||||
|
||||
@staticmethod
|
||||
def run():
|
||||
for pwm in PWM._pwms:
|
||||
pwm.next(time.time())
|
||||
|
||||
def __init__(self, pin, frequency=0, duty_cycle=0, lines=None, offset=None):
|
||||
self.duty_cycle = 0
|
||||
self.frequency = 0
|
||||
self.duty_period = 0
|
||||
self.period = 0
|
||||
self.running = False
|
||||
self.time_start = None
|
||||
self.state = Value.ACTIVE
|
||||
|
||||
self.set_frequency(frequency)
|
||||
self.set_duty_cycle(duty_cycle)
|
||||
|
||||
if isinstance(pin, tuple):
|
||||
self.lines, self.offset = pin
|
||||
else:
|
||||
self.lines, self.offset = gpiodevice.get_pin(pin, "PWM", OUTL)
|
||||
|
||||
PWM._add(self)
|
||||
|
||||
def set_frequency(self, frequency):
|
||||
if frequency == 0:
|
||||
return
|
||||
self.frequency = frequency
|
||||
self.period = 1.0 / frequency
|
||||
self.duty_period = self.duty_cycle * self.period
|
||||
|
||||
def set_duty_cycle(self, duty_cycle):
|
||||
self.duty_cycle = duty_cycle
|
||||
self.duty_period = self.duty_cycle * self.period
|
||||
|
||||
def start(self, duty_cycle=None, frequency=None, start_time=None):
|
||||
if duty_cycle is not None:
|
||||
self.set_duty_cycle(duty_cycle)
|
||||
|
||||
if frequency is not None:
|
||||
self.set_frequency(frequency)
|
||||
|
||||
self.time_start = time.time() if start_time is None else start_time
|
||||
|
||||
self.running = True
|
||||
|
||||
def next(self, t):
|
||||
if not self.running:
|
||||
return
|
||||
d = t - self.time_start
|
||||
d %= self.period
|
||||
new_state = Value.ACTIVE if d < self.duty_period else Value.INACTIVE
|
||||
if new_state != self.state:
|
||||
self.lines.set_value(self.offset, new_state)
|
||||
self.state = new_state
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
def __del__(self):
|
||||
PWM._remove(self)
|
||||
80
install.sh
80
install.sh
@@ -1,15 +1,15 @@
|
||||
#!/bin/bash
|
||||
LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'`
|
||||
LIBRARY_NAME=$(grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}')
|
||||
MODULE_NAME="grow"
|
||||
CONFIG_FILE=config.txt
|
||||
CONFIG_DIR="/boot/firmware"
|
||||
DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"`
|
||||
DATESTAMP=$(date "+%Y-%m-%d-%H-%M-%S")
|
||||
CONFIG_BACKUP=false
|
||||
APT_HAS_UPDATED=false
|
||||
RESOURCES_TOP_DIR=$HOME/Pimoroni
|
||||
VENV_BASH_SNIPPET=$RESOURCES_DIR/auto_venv.sh
|
||||
VENV_BASH_SNIPPET=$RESOURCES_TOP_DIR/auto_venv.sh
|
||||
VENV_DIR=$HOME/.virtualenvs/pimoroni
|
||||
WD=`pwd`
|
||||
WD=$(pwd)
|
||||
USAGE="./install.sh (--unstable)"
|
||||
POSITIONAL_ARGS=()
|
||||
FORCE=false
|
||||
@@ -18,7 +18,7 @@ PYTHON="python"
|
||||
|
||||
|
||||
user_check() {
|
||||
if [ $(id -u) -eq 0 ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
printf "Script should not be run as root. Try './install.sh'\n"
|
||||
exit 1
|
||||
fi
|
||||
@@ -61,7 +61,7 @@ warning() {
|
||||
find_config() {
|
||||
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
|
||||
CONFIG_DIR="/boot"
|
||||
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE"]; then
|
||||
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
|
||||
warning "Could not find $CONFIG_FILE!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -75,9 +75,9 @@ find_config() {
|
||||
}
|
||||
|
||||
venv_bash_snippet() {
|
||||
if [ ! -f $VENV_BASH_SNIPPET ]; then
|
||||
cat << EOF > $VENV_BASH_SNIPPET
|
||||
# Add `source $RESOURCES_DIR/auto_venv.sh` to your ~/.bashrc to activate
|
||||
if [ ! -f "$VENV_BASH_SNIPPET" ]; then
|
||||
cat << EOF > "$VENV_BASH_SNIPPET"
|
||||
# Add `source "$VENV_BASH_SNIPPET"` to your ~/.bashrc to activate
|
||||
# the Pimoroni virtual environment automagically!
|
||||
VENV_DIR="$VENV_DIR"
|
||||
if [ ! -f \$VENV_DIR/bin/activate ]; then
|
||||
@@ -92,21 +92,21 @@ EOF
|
||||
}
|
||||
|
||||
venv_check() {
|
||||
PYTHON_BIN=`which $PYTHON`
|
||||
PYTHON_BIN=$(which "$PYTHON")
|
||||
if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then
|
||||
printf "This script should be run in a virtual Python environment.\n"
|
||||
if confirm "Would you like us to create one for you?"; then
|
||||
if [ ! -f $VENV_DIR/bin/activate ]; then
|
||||
if [ ! -f "$VENV_DIR/bin/activate" ]; then
|
||||
inform "Creating virtual Python environment in $VENV_DIR, please wait...\n"
|
||||
mkdir -p $VENV_DIR
|
||||
/usr/bin/python3 -m venv $VENV_DIR --system-site-packages
|
||||
mkdir -p "$VENV_DIR"
|
||||
/usr/bin/python3 -m venv "$VENV_DIR" --system-site-packages
|
||||
venv_bash_snippet
|
||||
else
|
||||
inform "Found existing virtual Python environment in $VENV_DIR\n"
|
||||
fi
|
||||
inform "Activating virtual Python environment in $VENV_DIR..."
|
||||
inform "source $VENV_DIR/bin/activate\n"
|
||||
source $VENV_DIR/bin/activate
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
else
|
||||
exit 1
|
||||
@@ -119,11 +119,11 @@ function do_config_backup {
|
||||
CONFIG_BACKUP=true
|
||||
FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt"
|
||||
inform "Backing up $CONFIG_DIR/$CONFIG_FILE to $CONFIG_DIR/$FILENAME\n"
|
||||
sudo cp $CONFIG_DIR/$CONFIG_FILE $CONFIG_DIR/$FILENAME
|
||||
mkdir -p $RESOURCES_TOP_DIR/config-backups/
|
||||
cp $CONFIG_DIR/$CONFIG_FILE $RESOURCES_TOP_DIR/config-backups/$FILENAME
|
||||
sudo cp "$CONFIG_DIR/$CONFIG_FILE $CONFIG_DIR/$FILENAME"
|
||||
mkdir -p "$RESOURCES_TOP_DIR/config-backups/"
|
||||
cp $CONFIG_DIR/$CONFIG_FILE "$RESOURCES_TOP_DIR/config-backups/$FILENAME"
|
||||
if [ -f "$UNINSTALLER" ]; then
|
||||
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> $UNINSTALLER
|
||||
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> "$UNINSTALLER"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -134,8 +134,8 @@ function apt_pkg_install {
|
||||
for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do
|
||||
PACKAGE="${PACKAGES_IN[$i]}"
|
||||
if [ "$PACKAGE" == "" ]; then continue; fi
|
||||
printf "Checking for $PACKAGE\n"
|
||||
dpkg -L $PACKAGE > /dev/null 2>&1
|
||||
printf "Checking for %s\n" "$PACKAGE"
|
||||
dpkg -L "$PACKAGE" > /dev/null 2>&1
|
||||
if [ "$?" == "1" ]; then
|
||||
PACKAGES+=("$PACKAGE")
|
||||
fi
|
||||
@@ -149,7 +149,7 @@ function apt_pkg_install {
|
||||
fi
|
||||
sudo apt install -y $PACKAGES
|
||||
if [ -f "$UNINSTALLER" ]; then
|
||||
echo "apt uninstall -y $PACKAGES" >> $UNINSTALLER
|
||||
echo "apt uninstall -y $PACKAGES" >> "$UNINSTALLER"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -176,8 +176,8 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
*)
|
||||
if [[ $1 == -* ]]; then
|
||||
printf "Unrecognised option: $1\n";
|
||||
printf "Usage: $USAGE\n";
|
||||
printf "Unrecognised option: %s\n" "$1";
|
||||
printf "Usage: %s\n" "$USAGE";
|
||||
exit 1
|
||||
fi
|
||||
POSITIONAL_ARGS+=("$1")
|
||||
@@ -188,14 +188,14 @@ done
|
||||
user_check
|
||||
venv_check
|
||||
|
||||
if [ ! -f `which $PYTHON` ]; then
|
||||
printf "Python path $PYTHON not found!\n"
|
||||
if [ ! -f "$(which $PYTHON)" ]; then
|
||||
printf "Python path %s not found!\n" "$PYTHON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PYTHON_VER=`$PYTHON --version`
|
||||
PYTHON_VER=$($PYTHON --version)
|
||||
|
||||
printf "$LIBRARY_NAME Python Library: Installer\n\n"
|
||||
printf "%s Python Library: Installer\n\n" $LIBRARY_NAME
|
||||
|
||||
inform "Checking Dependencies. Please wait..."
|
||||
|
||||
@@ -221,21 +221,21 @@ if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval $CONFIG_VARS
|
||||
eval "$CONFIG_VARS"
|
||||
|
||||
RESOURCES_DIR=$RESOURCES_TOP_DIR/$LIBRARY_NAME
|
||||
UNINSTALLER=$RESOURCES_DIR/uninstall.sh
|
||||
|
||||
RES_DIR_OWNER=`stat -c "%U" $RESOURCES_TOP_DIR`
|
||||
RES_DIR_OWNER=$(stat -c "%U" "$RESOURCES_TOP_DIR")
|
||||
|
||||
if [[ "$RES_DIR_OWNER" == "root" ]]; then
|
||||
warning "\n\nFixing $RESOURCES_TOP_DIR permissions!\n\n"
|
||||
sudo chown -R $USER:$USER $RESOURCES_TOP_DIR
|
||||
sudo chown -R "$USER:$USER" "$RESOURCES_TOP_DIR"
|
||||
fi
|
||||
|
||||
mkdir -p $RESOURCES_DIR
|
||||
mkdir -p "$RESOURCES_DIR"
|
||||
|
||||
cat << EOF > $UNINSTALLER
|
||||
cat << EOF > "$UNINSTALLER"
|
||||
printf "It's recommended you run these steps manually.\n"
|
||||
printf "If you want to run the full script, open it in\n"
|
||||
printf "an editor and remove 'exit 1' from below.\n"
|
||||
@@ -254,14 +254,14 @@ apt_pkg_install "${APT_PACKAGES[@]}"
|
||||
if $UNSTABLE; then
|
||||
pip_pkg_install .
|
||||
else
|
||||
pip_pkg_install $LIBRARY_NAME
|
||||
pip_pkg_install "$LIBRARY_NAME"
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
success "Done!\n"
|
||||
echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||
echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> "$UNINSTALLER"
|
||||
fi
|
||||
|
||||
cd $WD
|
||||
cd "$WD" || exit 1
|
||||
|
||||
find_config
|
||||
|
||||
@@ -271,7 +271,7 @@ for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do
|
||||
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG_DIR/$CONFIG_FILE"* ]] || [[ "$CMD" == *"\$CONFIG_DIR/\$CONFIG_FILE"* ]]; then
|
||||
do_config_backup
|
||||
fi
|
||||
eval $CMD
|
||||
eval "$CMD"
|
||||
done
|
||||
|
||||
for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
|
||||
@@ -281,7 +281,7 @@ for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
|
||||
inform "Adding $CONFIG_LINE to $CONFIG_DIR/$CONFIG_FILE\n"
|
||||
sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG_DIR/$CONFIG_FILE
|
||||
if ! grep -q "^$CONFIG_LINE" $CONFIG_DIR/$CONFIG_FILE; then
|
||||
printf "$CONFIG_LINE\n" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE
|
||||
printf "%s\n" "$CONFIG_LINE" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -289,8 +289,8 @@ done
|
||||
if [ -d "examples" ]; then
|
||||
if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then
|
||||
inform "Copying examples to $RESOURCES_DIR"
|
||||
cp -r examples/ $RESOURCES_DIR
|
||||
echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER
|
||||
cp -r examples/ "$RESOURCES_DIR"
|
||||
echo "rm -r $RESOURCES_DIR" >> "$UNINSTALLER"
|
||||
success "Done!"
|
||||
fi
|
||||
fi
|
||||
@@ -300,7 +300,7 @@ printf "\n"
|
||||
if confirm "Would you like to generate documentation?"; then
|
||||
pip_pkg_install pdoc
|
||||
printf "Generating documentation.\n"
|
||||
$PYTHON -m pdoc $MODULE_NAME -o $RESOURCES_DIR/docs > /dev/null
|
||||
$PYTHON -m pdoc $MODULE_NAME -o "$RESOURCES_DIR/docs" > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
inform "Documentation saved to $RESOURCES_DIR/docs"
|
||||
success "Done!"
|
||||
|
||||
@@ -36,8 +36,10 @@ classifiers = [
|
||||
"Topic :: System :: Hardware",
|
||||
]
|
||||
dependencies = [
|
||||
"ltr559",
|
||||
"st7735>=0.0.5",
|
||||
"gpiodevice",
|
||||
"gpiod>=2.1.3",
|
||||
"ltr559>=1.0.0",
|
||||
"st7735>=1.0.0",
|
||||
"pyyaml",
|
||||
"fonts",
|
||||
"font-roboto"
|
||||
@@ -121,5 +123,11 @@ ignore = [
|
||||
|
||||
[tool.pimoroni]
|
||||
apt_packages = []
|
||||
configtxt = []
|
||||
commands = []
|
||||
configtxt = [
|
||||
"dtoverlay=spi0-cs,cs0_pin=14" # Re-assign CS0 from BCM 8 so that Grow can use it
|
||||
]
|
||||
commands = [
|
||||
"printf \"Setting up i2c and SPI..\\n\"",
|
||||
"sudo raspi-config nonint do_spi 0",
|
||||
"sudo raspi-config nonint do_i2c 0"
|
||||
]
|
||||
|
||||
@@ -18,31 +18,20 @@ class SMBusFakeDevice(MockSMBus):
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def cleanup():
|
||||
yield None
|
||||
try:
|
||||
del sys.modules['grow']
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
del sys.modules['grow.moisture']
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
del sys.modules['grow.pump']
|
||||
except KeyError:
|
||||
pass
|
||||
for module in ['grow', 'grow.moisture', 'grow.pump']:
|
||||
try:
|
||||
del sys.modules[module]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
def GPIO():
|
||||
"""Mock RPi.GPIO module."""
|
||||
GPIO = mock.MagicMock()
|
||||
# Fudge for Python < 37 (possibly earlier)
|
||||
sys.modules['RPi'] = mock.Mock()
|
||||
sys.modules['RPi'].GPIO = GPIO
|
||||
sys.modules['RPi.GPIO'] = GPIO
|
||||
yield GPIO
|
||||
del sys.modules['RPi']
|
||||
del sys.modules['RPi.GPIO']
|
||||
"""Mock gpiod module."""
|
||||
gpiod = mock.MagicMock()
|
||||
sys.modules['gpiod'] = gpiod
|
||||
yield gpiod
|
||||
del sys.modules['gpiod']
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
@@ -55,13 +44,13 @@ def spidev():
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
def smbus():
|
||||
"""Mock smbus module."""
|
||||
smbus = mock.MagicMock()
|
||||
smbus.SMBus = SMBusFakeDevice
|
||||
sys.modules['smbus'] = smbus
|
||||
yield smbus
|
||||
del sys.modules['smbus']
|
||||
def smbus2():
|
||||
"""Mock smbus2 module."""
|
||||
smbus2 = mock.MagicMock()
|
||||
smbus2.SMBus = SMBusFakeDevice
|
||||
sys.modules['smbus2'] = smbus2
|
||||
yield smbus2
|
||||
del sys.modules['smbus2']
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import time
|
||||
|
||||
|
||||
def test_pumps_actually_stop(GPIO, smbus):
|
||||
def test_pumps_actually_stop(gpiod, smbus2):
|
||||
from grow.pump import Pump
|
||||
|
||||
ch1 = Pump(channel=1)
|
||||
@@ -11,7 +11,7 @@ def test_pumps_actually_stop(GPIO, smbus):
|
||||
assert ch1.get_speed() == 0
|
||||
|
||||
|
||||
def test_pumps_are_mutually_exclusive(GPIO, smbus):
|
||||
def test_pumps_are_mutually_exclusive(gpiod, smbus2):
|
||||
from grow.pump import Pump, global_lock
|
||||
|
||||
ch1 = Pump(channel=1)
|
||||
@@ -29,7 +29,7 @@ def test_pumps_are_mutually_exclusive(GPIO, smbus):
|
||||
assert ch3.dose(speed=0.5, blocking=False) is False
|
||||
|
||||
|
||||
def test_pumps_run_sequentially(GPIO, smbus):
|
||||
def test_pumps_run_sequentially(gpiod, smbus2):
|
||||
from grow.pump import Pump, global_lock
|
||||
|
||||
ch1 = Pump(channel=1)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import mock
|
||||
|
||||
|
||||
def test_moisture_setup(GPIO, smbus):
|
||||
def test_moisture_setup(gpiod, smbus2):
|
||||
from grow.moisture import Moisture
|
||||
|
||||
ch1 = Moisture(channel=1)
|
||||
@@ -15,7 +15,7 @@ def test_moisture_setup(GPIO, smbus):
|
||||
])
|
||||
|
||||
|
||||
def test_moisture_read(GPIO, smbus):
|
||||
def test_moisture_read(gpiod, smbus2):
|
||||
from grow.moisture import Moisture
|
||||
|
||||
assert Moisture(channel=1).saturation == 1.0
|
||||
@@ -27,7 +27,7 @@ def test_moisture_read(GPIO, smbus):
|
||||
assert Moisture(channel=3).moisture == 0
|
||||
|
||||
|
||||
def test_pump_setup(GPIO, smbus):
|
||||
def test_pump_setup(gpiod, smbus2):
|
||||
from grow.pump import PUMP_PWM_FREQ, Pump
|
||||
|
||||
ch1 = Pump(channel=1)
|
||||
|
||||
Reference in New Issue
Block a user