Greenhouse Operations
Greenhouse operations API: bays, climate readings and plantings, sized for Pixeltable Cloud in pixeltable.toml
One app.py with 3 tables, 3 computed columns, 7 HTTP routes. Needs pixeltable[serve]>=0.7.14. Updated 2026-10-04.
app.py
"""Greenhouse Operations API built with Pixeltable.pxt schema update app.py greenhousepxt service run app.py greenhouse"""import pixeltable as pxtimport pixeltable.functions as pxtffrom pixeltable.serving import FastAPIRouterfrom udfs import bay_label, climate_band, vpd_kpa# ---- tables ----TableModel = pxt.model_base()class Bays(TableModel, name='bays', has_default_idxs=False):bay_id = pxt.Column(type=pxt.String, primary_key=True)zone: pxt.Stringcrop: pxt.String | Nonestatus: pxt.String # open / planted / restinglabel = bay_label(zone, bay_id, crop)__indexes__ = [pxt.BtreeIndex(zone), pxt.BtreeIndex(status), pxt.BtreeIndex(crop)]class Readings(TableModel, name='readings', has_default_idxs=False):id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)bay_id: pxt.Stringtaken_at: pxt.Stringtemp_c: pxt.Floathumidity_pct: pxt.Floatband = climate_band(temp_c, humidity_pct)vpd = vpd_kpa(temp_c, humidity_pct)__indexes__ = [pxt.BtreeIndex(bay_id)]class Plantings(TableModel, name='plantings', has_default_idxs=False):id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)bay_id: pxt.Stringcrop: pxt.Stringplanted_on: pxt.Stringstatus: pxt.String__indexes__ = [pxt.BtreeIndex(bay_id)]# ---- queries ----@pxt.querydef open_bays(zone: str):"""Open bays in a zone (zone + status indexes)."""return Bays.where((Bays.zone == zone) & (Bays.status == 'open')).select(Bays.bay_id, Bays.label).order_by(Bays.bay_id)@pxt.querydef bay_climate(bay_id: str):"""Readings for one bay, newest first."""return Readings.where(Readings.bay_id == bay_id).select(Readings.taken_at, Readings.temp_c, Readings.humidity_pct, Readings.band, Readings.vpd).order_by(Readings.taken_at, asc=False)# ---- routes ----greenhouse_api = FastAPIRouter(name='greenhouse_api')greenhouse_api.add_insert_route(Bays, path='/bays', inputs=[Bays.bay_id, Bays.zone, Bays.crop, Bays.status],outputs=[Bays.bay_id, Bays.label])greenhouse_api.add_update_route(Bays, path='/bays/status', inputs=[Bays.status, Bays.crop],outputs=[Bays.bay_id, Bays.status, Bays.label])greenhouse_api.add_insert_route(Readings, path='/readings',inputs=[Readings.bay_id, Readings.taken_at, Readings.temp_c, Readings.humidity_pct],outputs=[Readings.id, Readings.band, Readings.vpd])greenhouse_api.add_insert_route(Plantings, path='/plantings',inputs=[Plantings.bay_id, Plantings.crop, Plantings.planted_on, Plantings.status],outputs=[Plantings.id])greenhouse_api.add_compute_route(Readings, path='/climate-band', inputs=[Readings.temp_c, Readings.humidity_pct],outputs=[Readings.band, Readings.vpd])greenhouse_api.add_query_route(path='/bays/open', query=open_bays, method='get')greenhouse_api.add_query_route(path='/bays/climate', query=bay_climate, method='get')
What app.py declares
Each row links to its line.
- runs on insert
- stored
- index
- route
serves greenhouse_api
- POST/baysinsert, baystakes bay_id, zone, crop, status; returns bay_id, labelL67
- POST/bays/statusupdate, baystakes status, crop; returns bay_id, status, labelL69
- POST/readingsinsert, readingstakes bay_id, taken_at, temp_c, humidity_pct; returns id, band, vpdL71
- POST/plantingsinsert, plantingstakes bay_id, crop, planted_on, status; returns idL74
- POST/climate-bandcompute, readingstakes temp_c, humidity_pct; returns band, vpdL77
- GET/bays/openquery, open_baystakes zoneL79
- GET/bays/climatequery, bay_climatetakes bay_idL80
Run it
Needs uv. The routes then answer on port 8000.
terminal
git clone https://github.com/pierrebrunelle/pixeltable-greenhouse-bayscd pixeltable-greenhouse-baysuv syncuv run pxt schema update app.py greenhouseuv run python seed.py greenhouseuv run pxt service run app.py greenhouse --port 8000
Related apps
- Forge Heat CyclesForge heat-cycle registry API: B-tree indexes, chained risk UDFs, and parallel shop-floor writersShares: B-tree indexes, UDFs
- Climbing Route ToposClimbing-route topo registry API: photo uploads with thumbnails, grayscale copies, grade bands and index-backed crag lookupsShares: B-tree indexes, UDFs
- Warehouse Pick ListsWarehouse pick-list REST API: units, pick zones, waves and tote labels as incremental computed columns over Json order linesShares: B-tree indexes, UDFs