Skip to content

Python Packages

GreenThumb's shared Python code is two packages:

Package Repository Used by
greenthumb greenthumb-models The node API (microcontroller-api) and the cloud API (greenthumb-api)
greenthumb_rpi5 greenthumb-rpi5 The node API only. Hardware drivers, no database session.

greenthumb_rpi5 depends on greenthumb. The controller (microcontroller-api-client) installs neither: it talks to the node API over HTTP.


greenthumb

Module Contents
greenthumb.models SQLModel tables and their Create/Read/Update schemas, split by tier (tier0 users and properties, tier1 catalog, tier2 device configuration and cultivations, tier3 operational data, pi_only, enums). Columns that exist on only one tier are selected by the IS_CLOUD environment variable.
greenthumb.sync The Pi ↔ cloud sync: schemas (DTOs such as DeviceConfig and ComponentConfig), payload (builds the sync payload from the cloud database), apply (applies a payload to a session), contract (the sync wire contract version)
greenthumb.crud Database helpers (below)
greenthumb.api make_crud_router (below)
greenthumb.db The engine and the get_session dependency
greenthumb.config Typed service configuration
greenthumb.errors NotFoundError, ValidationError and the transaction decorators
greenthumb.error_handlers install_error_handlers (below)
greenthumb.templates Default JSONB skeletons for component_model.specs and device_component.instance_config

greenthumb.api and greenthumb.error_handlers need FastAPI, which comes with the api extra.

greenthumb.crud

Use these helpers instead of raw SQLAlchemy or hand-rolled CRUD routes.

from greenthumb.crud import get, get_by_id, save, update, delete, upsert
from greenthumb.db import get_session
Function Signature Use for
get get(session, Model, join=None, filters=None, distinct=False, order_by=None, one_or_none=False, limit=None) SELECT with optional filter dict, ordering, and limit
get_by_id get_by_id(session, Model, id) Fetch a single row by primary key (tuple for composite PKs)
save save(session, obj_or_list) INSERT one or many model instances; returns the saved object(s)
update update(session, Model, id, vals) UPDATE a row by PK from a Pydantic/SQLModel payload; raises NotFoundError if missing
delete delete(session, Model, id) DELETE a row by PK; raises NotFoundError if missing
upsert upsert(session, Model, data, *, policy="always", pk=None, filters=None, allow=None, preserve=None, flush=False) Insert-or-update under a write policy (below)

Pass filters= by keyword

join is the third positional argument of get, not filters. A filter dict passed positionally is read as a join and fails with SQLAlchemy's "Join target ... expected, got 'somefield'", which does not look like an argument-order mistake.

Filter dict syntax: {"field": value} for equality, {"field__gt": value} for comparisons (lt, lte, gt, gte, ne, in, contains, like).

upsert write policies:

Policy Behaviour Shortcut
"always" Incoming data wins upsert_simple
"absent" Insert only; an existing row is left alone insert_if_missing
"newer" Last write wins, by updated_at upsert_lww

greenthumb.api

make_crud_router(Model, CreateSchema, ReadSchema, UpdateSchema, prefix, tags=None) returns an APIRouter with the standard routes and handles commits and errors.

from greenthumb.api import make_crud_router
from greenthumb.models import PlantSpecies, PlantSpeciesCreate, PlantSpeciesRead, PlantSpeciesUpdate

router.include_router(
    make_crud_router(
        PlantSpecies, PlantSpeciesCreate, PlantSpeciesRead, PlantSpeciesUpdate,
        "/plant-species", tags=["Plant Species"],
    )
)

# GET    /plant-species         list
# GET    /plant-species/{id}    get one
# POST   /plant-species         create
# PUT    /plant-species/{id}    update
# DELETE /plant-species/{id}    delete

Models with a composite primary key get GET and DELETE on /{pk1}/{pk2}. Override a route only when it needs custom validation.

greenthumb.error_handlers

The app must call greenthumb.error_handlers.install_error_handlers(app) once, or a missing row returns 500 instead of 404.

from fastapi import FastAPI
from greenthumb.error_handlers import install_error_handlers

app = FastAPI()
install_error_handlers(app)

greenthumb_rpi5

Module Contents
component The Component, Sensor and Actuator base classes and the driver registry (@register_component)
sensor Sensor drivers (see Sensors)
actuator Actuator drivers (see Actuators)
bus Shared hardware handles (the I2C bus, ADS1115 chips) and the GPIO outputs
device DeviceManager: builds every driver from a DeviceConfig, reads sensors, runs actuator commands, takes the controller heartbeat and enters safety mode
schedule Time-window maths for schedule rules