mirror of
https://github.com/Crocmagnon/plant-badger.git
synced 2024-12-22 07:21:47 +01: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
|
invoke --list
|
||||||
# Start by getting your board id
|
# Start by getting your board id
|
||||||
inv list
|
inv list
|
||||||
# Then wipe the board
|
# Then provision the board
|
||||||
inv wipe <board_id>
|
inv provision-all
|
||||||
# Then run the initial setup
|
|
||||||
inv initial-setup <board_id>
|
|
||||||
# After that, just update the code when changes are made locally
|
# After that, just update the code when changes are made locally
|
||||||
inv update-code <board_id>
|
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
|
pre-commit
|
||||||
requests
|
requests
|
||||||
Pillow
|
Pillow
|
||||||
|
pyyaml
|
||||||
|
|
129
tasks.py
129
tasks.py
|
@ -1,6 +1,9 @@
|
||||||
|
import ast
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import yaml
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from invoke import task, Context
|
from invoke import task, Context
|
||||||
|
|
||||||
|
@ -13,41 +16,62 @@ MICROPYTHON_DEPENDENCIES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task(name="list")
|
||||||
def wipe(c: Context, board_id: str):
|
def list_boards(c: Context) -> 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 list(c: Context):
|
|
||||||
"""List connected boards with mpremote."""
|
"""List connected boards with mpremote."""
|
||||||
c.run("mpremote devs", pty=True, echo=True)
|
c.run("mpremote devs", pty=True, echo=True)
|
||||||
|
|
||||||
|
|
||||||
@task
|
@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 requests
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.insert(0, str(SRC_DIR))
|
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}
|
headers = {"Authorization": "Bearer " + HA_ACCESS_TOKEN}
|
||||||
res = requests.get(url, headers=headers)
|
res = requests.get(url, headers=headers)
|
||||||
data = res.json()
|
data = res.json()
|
||||||
|
@ -71,23 +95,28 @@ def download_image(c: Context):
|
||||||
image.save(image_path)
|
image.save(image_path)
|
||||||
|
|
||||||
|
|
||||||
@task(pre=[download_image])
|
@task
|
||||||
def initial_setup(c: Context, board_id: str):
|
def wipe(c: Context, board_id: str) -> None:
|
||||||
"""Install dependencies and copy project files to the board."""
|
"""Wipe the board with mpremote."""
|
||||||
wipe(c, board_id)
|
|
||||||
with c.cd(SRC_DIR):
|
|
||||||
if MICROPYTHON_DEPENDENCIES:
|
|
||||||
deps = " ".join(MICROPYTHON_DEPENDENCIES)
|
|
||||||
c.run(
|
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,
|
pty=True,
|
||||||
echo=True,
|
echo=True,
|
||||||
)
|
)
|
||||||
update_code(c, board_id)
|
print("Board wiped, waiting for it to reboot...")
|
||||||
|
time.sleep(3)
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def update_code(c: Context, board_id: str):
|
def update_code(c: Context, board_id: str) -> None:
|
||||||
"""Update code on the board."""
|
"""Update code on the board."""
|
||||||
with c.cd(SRC_DIR):
|
with c.cd(SRC_DIR):
|
||||||
c.run("find . -name '.DS_Store' -delete", pty=True, echo=True)
|
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,
|
pty=True,
|
||||||
echo=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