mirror of
https://github.com/Crocmagnon/plant-badger.git
synced 2024-09-07 22:57:35 +02:00
parent
884a9937bd
commit
ef110abebd
4 changed files with 137 additions and 43 deletions
|
@ -21,10 +21,8 @@ This will install dependencies required by PyCharm to run its MicroPython tools.
|
|||
invoke --list
|
||||
# Start by getting your board id
|
||||
inv list
|
||||
# Then wipe the board
|
||||
inv wipe <board_id>
|
||||
# Then run the initial setup
|
||||
inv initial-setup <board_id>
|
||||
# Then provision the board
|
||||
inv provision-all
|
||||
# After that, just update the code when changes are made locally
|
||||
inv update-code <board_id>
|
||||
```
|
||||
|
|
36
provisioning.yaml
Normal file
36
provisioning.yaml
Normal file
|
@ -0,0 +1,36 @@
|
|||
e6614864d336b436:
|
||||
HA_PLANT_ID: plant.peperomia_obtusifolia
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.peperomia_obtusifolia_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.peperomia_obtusifolia_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.peperomia_obtusifolia_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.peperomia_obtusifolia_illuminance
|
||||
e6614864d35f9934:
|
||||
HA_PLANT_ID: plant.hedera_helix
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.hedera_helix_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.hedera_helix_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.hedera_helix_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.hedera_helix_illuminance
|
||||
e6614864d3417f36:
|
||||
HA_PLANT_ID: plant.calathea_makoyana
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.calathea_makoyana_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.calathea_makoyana_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.calathea_makoyana_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.calathea_makoyana_illuminance
|
||||
ficus:
|
||||
HA_PLANT_ID: plant.ficus
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.ficus_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.ficus_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.ficus_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.ficus_illuminance
|
||||
aloe:
|
||||
HA_PLANT_ID: plant.aloe_vera
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.aloe_vera_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.aloe_vera_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.aloe_vera_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.aloe_vera_illuminance
|
||||
toupic:
|
||||
HA_PLANT_ID: plant.senecio_himalaya
|
||||
HA_PLANT_MOISTURE_SENSOR: sensor.senecio_himalaya_soil_moisture
|
||||
HA_PLANT_TEMPERATURE_SENSOR: sensor.senecio_himalaya_temperature
|
||||
HA_PLANT_CONDUCTIVITY_SENSOR: sensor.senecio_himalaya_conductivity
|
||||
HA_PLANT_ILLUMINANCE_SENSOR: sensor.senecio_himalaya_illuminance
|
|
@ -4,3 +4,4 @@ black
|
|||
pre-commit
|
||||
requests
|
||||
Pillow
|
||||
pyyaml
|
||||
|
|
129
tasks.py
129
tasks.py
|
@ -1,6 +1,9 @@
|
|||
import ast
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from PIL import Image
|
||||
from invoke import task, Context
|
||||
|
||||
|
@ -13,41 +16,62 @@ MICROPYTHON_DEPENDENCIES = [
|
|||
]
|
||||
|
||||
|
||||
@task
|
||||
def wipe(c: Context, board_id: str):
|
||||
"""Wipe the board with mpremote."""
|
||||
c.run(
|
||||
f'mpremote connect id:{board_id} exec --no-follow "'
|
||||
"import os, machine, rp2;"
|
||||
"os.umount('/');"
|
||||
"bdev = rp2.Flash();"
|
||||
"os.VfsLfs2.mkfs(bdev, progsize=256);"
|
||||
"vfs = os.VfsLfs2(bdev, progsize=256);"
|
||||
"os.mount(vfs, '/');"
|
||||
'machine.reset()"',
|
||||
pty=True,
|
||||
echo=True,
|
||||
)
|
||||
print("Board wiped, waiting for it to reboot...")
|
||||
time.sleep(3)
|
||||
print("Done!")
|
||||
|
||||
|
||||
@task
|
||||
def list(c: Context):
|
||||
@task(name="list")
|
||||
def list_boards(c: Context) -> None:
|
||||
"""List connected boards with mpremote."""
|
||||
c.run("mpremote devs", pty=True, echo=True)
|
||||
|
||||
|
||||
@task
|
||||
def download_image(c: Context):
|
||||
def provision_all(c: Context) -> None:
|
||||
"""Provision all connected boards sequentially."""
|
||||
# Here's an example output of `mpremote devs`:
|
||||
# /dev/cu.Bluetooth-Incoming-Port None 0000:0000 None None
|
||||
# /dev/cu.usbmodem101 e6614864d35f9934 2e8a:0005 MicroPython Board in FS mode
|
||||
# /dev/cu.usbmodem112201 e6614864d3417f36 2e8a:0005 MicroPython Board in FS mode
|
||||
|
||||
output = subprocess.run(["mpremote", "devs"], stdout=subprocess.PIPE).stdout.decode(
|
||||
"utf-8"
|
||||
)
|
||||
lines = output.splitlines()
|
||||
ids = []
|
||||
for line in lines:
|
||||
if "Bluetooth" not in line:
|
||||
ids.append(line.split()[1])
|
||||
|
||||
for board_id in ids:
|
||||
provision(c, board_id)
|
||||
|
||||
|
||||
@task
|
||||
def provision(c: Context, board_id: str) -> None:
|
||||
"""Install dependencies and copy project files to the board."""
|
||||
prepare(board_id)
|
||||
download_image(c, board_id)
|
||||
wipe(c, board_id)
|
||||
with c.cd(SRC_DIR):
|
||||
if MICROPYTHON_DEPENDENCIES:
|
||||
deps = " ".join(MICROPYTHON_DEPENDENCIES)
|
||||
c.run(
|
||||
f"mpremote connect id:{board_id} " f"mip install {deps}",
|
||||
pty=True,
|
||||
echo=True,
|
||||
)
|
||||
update_code(c, board_id)
|
||||
|
||||
|
||||
@task
|
||||
def download_image(c: Context, board_id: str) -> None:
|
||||
"""Download and prepare the proper plant picture for the board."""
|
||||
import requests
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(SRC_DIR))
|
||||
from secrets import HA_ACCESS_TOKEN, HA_BASE_URL, HA_PLANT_ID
|
||||
from secrets import HA_ACCESS_TOKEN, HA_BASE_URL
|
||||
|
||||
url = HA_BASE_URL + "/states/" + HA_PLANT_ID
|
||||
provisioning = get_provisioning(board_id)
|
||||
|
||||
url = HA_BASE_URL + "/states/" + provisioning["HA_PLANT_ID"]
|
||||
headers = {"Authorization": "Bearer " + HA_ACCESS_TOKEN}
|
||||
res = requests.get(url, headers=headers)
|
||||
data = res.json()
|
||||
|
@ -71,23 +95,28 @@ def download_image(c: Context):
|
|||
image.save(image_path)
|
||||
|
||||
|
||||
@task(pre=[download_image])
|
||||
def initial_setup(c: Context, board_id: str):
|
||||
"""Install dependencies and copy project files to the board."""
|
||||
wipe(c, board_id)
|
||||
with c.cd(SRC_DIR):
|
||||
if MICROPYTHON_DEPENDENCIES:
|
||||
deps = " ".join(MICROPYTHON_DEPENDENCIES)
|
||||
@task
|
||||
def wipe(c: Context, board_id: str) -> None:
|
||||
"""Wipe the board with mpremote."""
|
||||
c.run(
|
||||
f"mpremote connect id:{board_id} " f"mip install {deps}",
|
||||
f'mpremote connect id:{board_id} exec --no-follow "'
|
||||
"import os, machine, rp2;"
|
||||
"os.umount('/');"
|
||||
"bdev = rp2.Flash();"
|
||||
"os.VfsLfs2.mkfs(bdev, progsize=256);"
|
||||
"vfs = os.VfsLfs2(bdev, progsize=256);"
|
||||
"os.mount(vfs, '/');"
|
||||
'machine.reset()"',
|
||||
pty=True,
|
||||
echo=True,
|
||||
)
|
||||
update_code(c, board_id)
|
||||
print("Board wiped, waiting for it to reboot...")
|
||||
time.sleep(3)
|
||||
print("Done!")
|
||||
|
||||
|
||||
@task
|
||||
def update_code(c: Context, board_id: str):
|
||||
def update_code(c: Context, board_id: str) -> None:
|
||||
"""Update code on the board."""
|
||||
with c.cd(SRC_DIR):
|
||||
c.run("find . -name '.DS_Store' -delete", pty=True, echo=True)
|
||||
|
@ -96,3 +125,33 @@ def update_code(c: Context, board_id: str):
|
|||
pty=True,
|
||||
echo=True,
|
||||
)
|
||||
|
||||
|
||||
def prepare(board_id: str) -> None:
|
||||
"""Update secrets.py with the correct values for the board."""
|
||||
provisioning = get_provisioning(board_id)
|
||||
|
||||
with (SRC_DIR / "secrets.py").open() as f:
|
||||
secrets = f.read()
|
||||
|
||||
secrets = ast.parse(secrets)
|
||||
for node in secrets.body:
|
||||
if isinstance(node, ast.Assign):
|
||||
for target in node.targets:
|
||||
var_name = target.id
|
||||
if var_name in provisioning:
|
||||
node.value = ast.Constant(provisioning[var_name])
|
||||
|
||||
with (SRC_DIR / "secrets.py").open("w") as f:
|
||||
f.write(ast.unparse(secrets))
|
||||
|
||||
|
||||
def get_provisioning(board_id: str) -> dict[str, str]:
|
||||
# load provisioning.yaml
|
||||
with (BASE_DIR / "provisioning.yaml").open() as f:
|
||||
provisioning = yaml.safe_load(f)
|
||||
provisioning = provisioning.get(board_id)
|
||||
if not provisioning:
|
||||
msg = "Couldn't find board %s in provisioning.yaml" % board_id
|
||||
raise ValueError(msg)
|
||||
return provisioning
|
||||
|
|
Loading…
Reference in a new issue