Skip to Content

Tiles: the building blocks of a Flow

Available

  • The seven platform tiles
  • GET /v1/tiles, GET /v1/tiles/{id}
  • The self-describing GET /v1/tiles/{id}/spec

Planned

  • Creating and forking your own tiles (POST /v1/tiles, /fork, /test)
  • Python tiles

A Flow is a grid of tiles evaluated left to right; each tile turns inputs (sensor readings, weather, events, or the previous tile’s output) into an output series, and the last tile’s output decides whether the Flow triggers. In Growth Engine every tile, including the built-in ones, is a database record, so tiles can evolve without a release and you can add your own.

What a tile definition contains

FieldMeaning
key, name, description, version, isLatestidentity; versions are immutable; a Flow pins the version it was built with. Platform tiles gain a new version whenever Soiltech updates them; your flows keep running the pinned version until you re-save
paramsSchemaJSON Schema for the tile’s parameters; the GUI renders the tile’s configuration dialog from it
inputswhat data the tile needs (sensor metrics, weather variables, events); the Flow’s data requirements are derived from this
outputKindnumeric, boolean, probability or events
uiicon, color, palette group/order, short name, summary template, badges, help
language, scriptthe runtime logic (TypeScript/JavaScript or Python), run in the Engine’s sandbox

Built-in (platform) tiles

Built in and available to every tenant:

KeyNameDoes
gddGrowing degree daysdaily heat units above a base temperature (simple or modified with upper/lower cutoffs) over a (seasonal) date window; triggers at a threshold, optionally freezing once reached; Variable Start / Variable Stop can tie the window to another tile (below)
chChill hourshours below a base temperature (default 7.2 °C) over a window; triggers at a threshold; supports the same Variable Start / Variable Stop gates
bvSensor variablea sensor metric (air/soil temperature, humidity, matric potential, VWC, leaf wetness, rain) vs a threshold; modes: any / latest / overlap with the previous tile / rolling window, optional sustained duration
wvWeather variablethe same machinery on weather: temperature, humidity, rain (with accumulation), solar, daily ET₀, VPD (air, a fixed leaf-temperature offset, or a leaf-temperature sensor reading), wind speed, wind direction (45° sector), dew point; day/night filter; an optional forecast offset evaluates against the coming days (up to 15), so a flow can trigger on forecast conditions such as a frost ahead
accProbability0–100 % from the previous tile, in time mode (steps up while it stays triggered, down while it doesn’t) or value mode (scales a value between its 0 % and 100 % points, with one band per rule combined by weight, so several earlier tiles can blend into one probability); the flow’s probability and colour come from the first acc. In time mode a scout-events tile feeding the probability holds it flat rather than stepping, so pair scout events with value mode or a sensor or weather tile
twTime windowpasses the previous tile’s triggers only inside fixed dates, a rolling window from a start date, or the last N hours/days
seScout eventsplanted / harvested / irrigated / custom events with each-time, running-total or yes/no thresholds, recency or sustained duration; or beacon hardware events — location moved and bumped trigger whenever the flow runs, low battery triggers when the beacon voltage drops to a threshold

Every data-consuming tile (gdd, ch, bv, wv, se) has a dataSource parameter: generate (the default today: deterministic demo data so you can build and run flows before your data is connected) or adapter (your own data via adapters; a run with adapter fails with a clear message until adapters ship).

Any tile can also be marked report only: it still runs and shows in results, but it does not count toward the flow’s trigger or alerts. Use it for a reading you want on the report without letting it decide whether the flow fires.

The gdd and ch tiles can start or stop accumulating relative to another tile instead of a fixed calendar date. With Variable Start, accumulation begins on the day a chosen upstream gate tile first triggers; with Variable Stop, it ends on the day the gate first triggers. The gate is an earlier tile in the chain that can trigger (by default the one just before it, or you can pick a specific one). The chosen day is remembered per target and stays fixed once set, so a later change in the gate does not move an already-anchored window. If the gate has not triggered yet, the tile simply waits and does not accumulate.

The tile spec: build your own tile UI

Every tile is self-describing. GET /v1/tiles/{id}/spec returns everything a client needs to show the tile in a palette and to build its configuration dialog, with no tile-specific code on your side. (GET /v1/tiles/{id} returns the raw definition incl. the JSON Schema; /spec flattens it.)

{ "id": "5d3c…", "key": "gdd", "version": 1, "name": "Growing degree days", "description": "Accumulates heat units above a base temperature over a date window and triggers at a threshold.", "outputKind": "numeric", "isPlatform": true, "language": "typescript", "inputs": [ { "kind": "weather", "variables": ["tempmax", "tempmin"], "when": { "source": "weather" } }, { "kind": "series", "metrics": ["temperature.ambient"], "when": { "source": "sensor" } } ], "ui": { "icon": { "kind": "mdi", "value": "WbSunny" }, "color": { "light": "#f59e0b", "dark": "#fbbf24" }, "group": "Weather", "order": 10, "shortName": "GDD", "summaryTemplate": "GDD base {{tBase}}°C ≥ {{threshold}}", // shown on the grid cell "badges": { "numeric": true }, "help": { "markdown": "Growing degree days (GDD) measure accumulated warmth…" } }, "params": [ { "name": "dataSource", "type": "string", "title": "Data source", "widget": "select", "group": "Data", "order": 0, "default": "generate", "enum": ["generate", "adapter"], "options": [ { "value": "generate", "label": "Generated demo data" }, { "value": "adapter", "label": "Adapter fetch (not yet available)" } ] }, { "name": "tBase", "type": "number", "title": "Base temperature", "unit": "°C", "widget": "slider", "minimum": -20, "maximum": 40, "step": 0.1, "default": 10, "group": "Method", "order": 3 }, { "name": "tUpper", "type": "number", "title": "Upper cutoff", "unit": "°C", "showIf": { "isSimple": [false] }, "group": "Method", "order": 4 }, { "name": "threshold", "type": "number", "title": "Trigger at", "unit": "GDD", "required": true, "default": 250, "group": "Trigger", "order": 6 }, { "name": "startDate", "type": "string", "format": "date", "widget": "date", "title": "Start date", "group": "Window", "order": 7 } // … ], "paramsSchema": { "type": "object", "properties": { "tBase": { "type": "number", "x-ui-unit": "°C", "x-ui-widget": "slider" /* … */ } } }, "notes": [ "Seasonal windows wrap the year; …" ] }

Parameter entries carry the JSON-Schema type, title/description, required, default, the expected range (minimum/maximum/step), format (date), enum + options (value/label, optionally per-option unit), unit or unitFrom (unit follows another param, e.g. the chosen metric), widget (select, switch, slider, number, date, text, metric-picker, rules, …), group/order for layout, showIf ({param: [values]}) for dependent fields, and any further x-ui-* hints verbatim under ui. params is sorted by order. The same information is embedded in the definition’s JSON Schema as x-ui-* keywords (paramsSchema is returned too), so a generic JSON-Schema form renderer works. The Engine validates flow parameters against that schema on save and returns field-level errors (errors[].loc like tiles[0].params.threshold).

Your own tiles (planned)

Create a tile with POST /v1/tiles (scope tiles:manage), or fork a platform tile as a starting point; give it a paramsSchema, and provide the script:

// evaluate(ctx, params, inputs, upstream) → TileOutput import type { TileCtx, TileInputs, TileOutput } from "@ge/tiles"; // helpers: durationMs, compare, resolveWindow, evaluateSeries… export default function evaluate(ctx: TileCtx, params: { frostC: number }, inputs: TileInputs, upstream: TileOutput | null): TileOutput { const temps = inputs.series?.["temperature.ambient"] ?? []; // [[ts (ms), value], ...] const points = temps.map(([ts, t]) => ({ ts, value: t, isTriggered: t <= params.frostC })); return { kind: "boolean", points, isTriggered: points.at(-1)?.isTriggered ?? false, lastValue: points.at(-1)?.value }; }

Python tiles export def evaluate(ctx, params, inputs, upstream) with the same shapes as dicts. Tile scripts are pure functions (no network, and no clock beyond ctx.now), so results are reproducible. Test a tile with sample inputs via POST /v1/tiles/{id}/test, then it appears in your palette next to the platform tiles.

Endpoints

Available (user gate: GUI sessions and tenant keys)

  • GET /v1/tiles (latest versions visible to you; ?allVersions=true for all)
  • GET /v1/tiles/{id}
  • GET /v1/tiles/{id}/spec

Planned (scope tiles:manage)

  • POST /v1/tiles
  • POST /v1/tiles/{id}/versions
  • PATCH /v1/tiles/{id}
  • POST /v1/tiles/{id}/fork
  • POST /v1/tiles/{id}/test