Add provisioning file.

Close #5
This commit is contained in:
Gabriel Augendre 2023-03-13 21:59:56 +01:00
parent 884a9937bd
commit ef110abebd
4 changed files with 137 additions and 43 deletions

View file

@ -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
View 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

View file

@ -4,3 +4,4 @@ black
pre-commit
requests
Pillow
pyyaml

137
tasks.py
View file

@ -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)
c.run(
f"mpremote connect id:{board_id} " f"mip install {deps}",
pty=True,
echo=True,
)
update_code(c, board_id)
@task
def wipe(c: Context, board_id: str) -> None:
"""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 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