fieldmesh

modules/soilwater/ — soil-moisture advisory (layer 2)#

The second layer-2 module (spec v0.1 §11), and Q12's other read-only choice: every morning, and again early in the afternoon, for every bed, what to do about water — and when it cannot say, why not. A season of its advice, kept in the log as decision events (§7.7), is the irrigation module's evidence base before that module exists.

advice when
water now, and how many millimetres the root zone has lost its crop's share of the plant-available water (FAO-56's p, a third by default): back up to field capacity
rain coming it is due, but 10 mm or more is forecast in the next 24 hours, enough to leave it short of half its water by tomorrow
water soon at the rate it has dried since it was last wetted, it is due within two days
too wet wetter than field capacity: water is draining past the roots
enough and how much of the available water is left
no probe nothing is installed within the root zone
unseen no reading for six hours
untrusted the newest readings failed the health checks, or read drier than the bed's soil can be (below)
by the weather: water now, rain coming, enough, or the station unseen the bed has no probe, or its probes have gone silent or untrusted, and the farm's station is given (below)
out of the soil, beside the morning's advice a probe reads drier than its soil can be, and the advice came from the bed's other probes or the weather (below)
layered, beside the morning's advice two of its probes learned field capacities far apart, and the bed was given one soil's values (below)

The arithmetic is FAO-56's root-zone balance read from the probes rather than modelled: TAW = (field capacity − wilting point) × root depth, depletion Dr = (field capacity − θ) × root depth, water when Dr ≥ p × TAW, Dr millimetres. Each probe's deficit counts on its own: soil past field capacity under one probe is draining and does not fill the dry soil under another, so a wet subsoil does not cancel a dry top. The soil's limits come from a soil test or the survey (tools/soil_survey.py), by layer where the soil changes with depth. The field capacity is then learned from the probes themselves (below).

The probes are found, not configured. A bed's probes are the soil-moisture series whose device was installed in it when the reading was taken (§4.1), at the depth its installation says. Each stands for the soil from halfway to the probe above to halfway to the one below, and only the part inside the root zone counts. A probe moved from one bed to another changes both beds' advice on the day it moves; one deeper than the roots counts for nothing.

Two looks a day. The advice is given at 04:00, before the morning's watering, and again at 13:00 (SECOND_CHECK_HOUR; deploy/systemd/ runs the module at both). A bed with little water to spare, a lawn or a shallow topsoil over clay, can be past half of it by the afternoon of a hot day; the second look waters it then rather than the next morning. The afternoon's look counts the morning's watering, recorded or advised, so it never asks for the same water twice.

Only readings that had passed the health checks by that morning count (State.quality_as_of), so a stuck or drifting probe never waters a bed. A probe gone silent while others still read stands for its slice at its last good reading, less what the others have dried since, and the decision lists it (carried); when all are silent, the bed is carried on the weather. A series not in a volume fraction (FRACTION_UNITS) is not one of the bed's probes. Readings are counted an hour at a time, however often a probe reads. Rain coming is read from a forecast datastream in the log (Q26), as it stood that morning (State.forecast(as_of=...)), so the forecast an advice rested on sits beside it.

Backtested#

§11 asks for it before anything goes live. The advisory is backtested on a virtual farm (sim/physics.py): a bed's soil in layers that drain, soak up water and pass it between them by Darcy's law; a crop that grows through FAO-56's curve, uses its water whole until its own p and draws it most from the top of its roots; bare soil that dries in the sun; probes read through the same health checks a farm's are. The harness is backtest.py, backtest with probes and weather_backtest by the weather, kept apart from the advisory, which never imports the simulator. How the virtual farm's soil and crop compare with the models agriculture uses, and how they were checked against HYDRUS-1D and a real station's probes, is in soil models.

Scored by the water given and what drained past the roots; the hours the bed had lost half its available water; and, since 2026-10-17, the transpiration its dryness cost the crop (FAO-33's measure of lost yield), the hours it used less than its full demand, and the hours its root zone held less than a tenth air; since 2026-10-18, the hours it held less than a twentieth, waterlogged (a tenth is a few points above field capacity in these silt loams), what ran off, and what the whole soil held at the end against the start, so a bed that drew on the water below its roots shows it (backtest.Score). Watered by probes or by the weather, the water goes on at 12 mm an hour for as long as it takes.

On the farm as it is written (backtest.NOMINAL): McAllister Farm's recorded summer of 2013, June to August, a 30 cm root zone in a silt loam, the drill's forecasts in the log, the advisory followed to the letter, beside the drip timer most farms run (sim/irrigation_drill.py):

water applied drained past the roots hours stressed ≥ 0.5 mean stress
drip timer, every second morning 552 mm 552 mm 0 0.06
timer, skipping when rain is forecast 444 mm 444 mm 0 0.06
the advisory 221 mm 221 mm 0 0.12

60% less water and 60% less drained, with no hour of real stress (since 2026-10-18; the figures before it are in the record). The cost is a drier root zone on average, which is the trade a refill point makes, and the reason p is the crop's to set. The tests run this and a dozen other single summers once each (BACKTESTS in test_soilwater.py), say what must stay true of them, and hold their exact figures in golden.json: a change that moves one shows there.

On a farm that departs from what the advice is told (backtest.VirtualFarm): its crop using more or less than its curve, running early or late, rooting deeper or shallower; its soil's limits off; each probe off by its own calibration error; its crop's roots making up for each other as far as a strength drawn for it lets them; its station's sensors and siting off; its water loss Penman–Monteith with the day's own wind, where the farm's station assumes 2 m/s; its rain forecasts missing whole days and forecasting spells that do not come, and read as they stood each morning. The advisory's defaults were tried so in trials.py, on McAllister's summers and on Davis, California's, dry and windier, then at two sites kept sealed, each on a farm drawn for it (below). How each table here came to be, and the advice changed for what it showed, from 2026-10-11, is in the backtests' record.

What the trials show#

The advisory was tried by trials.py on a virtual farm that departs from what the advice is told (backtest.VirtualFarm, above): ten beds, through the recorded summers of 1991 to 2025 at four sites, each summer on a farm drawn for it. Its choices were tried at McAllister Farm, Tennessee, humid, and Davis, California, dry and windier (5,880 backtests on code 51dffc9, 2026-10-18; the hot-day refill point's run again on its fix, as committed in 735d41f), and read by rules written before the trial ran. Then the advice as chosen, and the hardware those rules recommended, were run once at two sites kept sealed until then: Bushland, Texas, semi-arid, windy and high, and Tifton, Georgia, humid and hot (1,330 backtests, code 735d41f). Each bed as built, the mean of a summer: the water given, the transpiration the crop could not draw for want of water and its share of what it would have drawn, and the hours it was waterlogged (under a twentieth of air):

bed McAllister Davis Bushland Tifton
bucket, its probe 249 mm, 0.3 (0.1%), 0 h 692 mm, 15.8 (2.1%), 0 h 542 mm, 19.4 (2.6%), 0 h 273 mm, 0.6 (0.1%), 0 h
bucket, by the weather 297 mm, 0.2 (0.0%), 0 h 711 mm, 14.3 (1.8%), 0 h 529 mm, 27.4 (3.4%), 0 h 245 mm, 4.7 (0.8%), 0 h
tomatoes, probes 568 mm, 0.1 (0.0%), 12 h 1135 mm, 7.3 (1.0%), 1 h 1023 mm, 5.0 (0.7%), 3 h 550 mm, 0.4 (0.1%), 11 h
tomatoes, by the weather 559 mm, 0.2 (0.1%), 11 h 1213 mm, 0.8 (0.1%), 5 h 968 mm, 3.9 (0.5%), 4 h 550 mm, 0.2 (0.0%), 11 h
lettuce, probes 266 mm, 0.2 (0.1%), 9 h 504 mm, 11.5 (3.7%), 1 h 508 mm, 9.4 (2.5%), 2 h 286 mm, 0.3 (0.1%), 6 h
lettuce, by the weather 282 mm, 0.1 (0.1%), 10 h 608 mm, 0.6 (0.2%), 5 h 523 mm, 4.6 (1.2%), 5 h 334 mm, 0.2 (0.1%), 9 h
lawn, its probe 162 mm, 0.0 (0.0%), 32 h 515 mm, 54.8 (9.0%), 0 h 406 mm, 23.5 (3.4%), 12 h 161 mm, 0.3 (0.1%), 34 h
lawn, by the weather 301 mm, 0.0 (0.0%), 61 h 689 mm, 1.7 (0.2%), 37 h 520 mm, 5.5 (0.7%), 41 h 253 mm, 0.3 (0.1%), 61 h
yard, its probes 244 mm, 1.0 (0.3%), 6 h 598 mm, 28.4 (4.3%), 0 h 485 mm, 18.1 (2.5%), 2 h 249 mm, 1.5 (0.3%), 9 h
tomatoes over a dense pan, probes 559 mm, 0.7 (0.2%), 36 h 1124 mm, 13.3 (1.9%), 5 h 985 mm, 9.3 (1.2%), 9 h 592 mm, 1.0 (0.2%), 37 h

Every choice against the bed as built, with its 90% interval, site by site, is in the backtests' record.

What measuring more would buy#

The bed's own soil#

A soil survey maps the native soil. A landscaped yard, a raised bed or a market garden on an old house lot is often something else: topsoil brought in over a subsoil the builders compacted, sod with its own soil over the yard's, a compost mix over the ground. Set up with the survey's values, the advisory misjudges such a bed. So each probe learns its own field capacity from its own drain-downs (learned_capacity):

Until then, and for a value within 0.03 of its soil's wilting point, the configured value stands. Bed(learn=False) turns learning off.

Learning reads some soils wrong. Where drainage slows by degrees, the fit's line runs back to the peak high: in wet summers the trials' crop beds learned 0.015 to 0.05 above their soil's capacity as each probe reads it. In a hot, dry lawn the waterings spread on into drier soil below, each drains back lower, and what is learned sinks with them. A tension sensor measures the capacity instead, and is the cure (the lawn at Davis lost 47 mm a summer fewer with one). Bed.learn_within holds learning within a distance of the configured value: at 0.05 it cut the lawn's loss at Davis by 15 mm, but added half a millimetre to lettuce's, and it stops a bed learning a survey that is badly wrong (the builder's yard below was short twice as long), so it is an option. When it holds a probe back, a note says so:

bed 3's probe at 20 cm drains back to 22% water after a soaking, but the bed is set up for a soil that holds 30%, and the advice goes no further than 5 points from that. If the soil's values are a survey's or a guess, give the bed a soil test's, run a soak test, or put a tension sensor beside the probe. What it learns is the level the bed actually holds a day or two after a soaking, the in-place sense of field capacity (Twarakavi, Sakai and Šimůnek 2009, Water Resources Research 45, doi:10.1029/2009WR007944).

A layered bed. Two probes whose learned field capacities differ by 0.08 or more (LAYER_GAP) are in two soils. When the bed was given one soil's values, the advisory then writes a second decision beside the morning's, soil-layered:

bed 3's soil is layered: its probe at 5 cm drains back to 23% water after a soaking, the one at 12 cm to 40%. One soil's values cannot describe it, and the advice may wait too long for the layer whose water the roots cannot take. Give the bed each layer's field capacity and wilting point, from a soil test of each layer or the soil survey's horizons.

Bed.layers takes those values: (bottom_m, field_capacity, wilting_point), top down. Each probe's slice of the root zone is then judged at its own soil's limits, and the note stops. tools/soil_survey.py gives the survey's values by depth.

Looking ahead to tomorrow. The advice also waters a bed that, drying as it has been, would pass Bed.short_fraction of its water (half) before the next look.

A tension sensor beside a probe measures field capacity instead of learning it. Each hour the two at one depth give a point on the soil's own retention curve. Over the tensions a Watermark is calibrated for, 10 to 75 kPa (sdk/tension.py), water falls close to linearly with the logarithm of tension, so the points are fitted and field capacity read at Bed.capacity_kpa, 33 kPa by default (retention.py). It needs 24 hours of points spanning a factor of two in tension and reaching to within half again of 33, and no soaking. A measured value stands before a learned one for that probe's slice; the decision says which ("how": "measured").

What the backtests showed (record): on the builder's yard, sandy loam over compacted clay, one probe in the topsoil on the survey's values drowned it, and learning cut the water by three fifths; with a probe in each layer, learning alone did not help, and each layer's values did. Learning leaves a correctly set-up bed alone, to the millimetre. A probe that reads low is forgiven, since its drain-downs read low too; one that reads high is not, since it is watered too little to learn from, and a second probe in the bed is what catches it (soil sensors).

A crop that grows#

A seedling draws little water from a few centimetres of soil; a full canopy draws more than the reference grass, from deep. A bed told its crop and the day it was sown (Bed.crop, Bed.planted) follows FAO-56's curve for that crop through its season (crop_day), with the 2025 revision's values (FAO-56 Rev.1, data/fao56rev-crops.csv) and the 1998 edition's stage days (data/fao56-crops.csv):

A young crop is watered at a fifth of its water gone (Bed.young_fraction) through its initial stage and until its roots pass 25 cm (YOUNG_ROOTS_MM), its few roots in the soil the sun dries. Among older plantings, that is the refill point of the young one's roots alone. Without the root depth, the crops' shortages by probes fell in the days just after the initial stage ended, the roots still the 15 cm they were sown with.

Lawns: the 1998 values by default, and the revision's as turf_lawn. The revision gives a mown lawn 0.50 to 0.70 through its season (Table 6.4), against the 1998 edition's 0.95 for cool-season turf; turf_lawn holds its 0.70 all year. By a probe the value makes no difference; by the weather, a lawn told 0.70 that truly uses 0.95 went short in nearly every summer on a sandy yard, so turf_cool stays the default. A lawn with a probed bed nearby as its reference can learn that its rate is faster still (below).

The day's root depth sets the root zone the probes are weighed over and the water it holds. A probe below a seedling's roots counts for nothing until the roots reach it, nor does a probe at the root zone's very bottom (weights): it reads the soil the roots are only reaching.

What the backtests showed (record): not knowing the crop costs it, by the weather and by probes; knowing it, by the weather neither tomatoes nor lettuce was short in any summer of thirty on the farm as written.

By the season's warmth#

The 1998 edition's stages are days, for a typical season; a warm year runs a crop through them sooner and a cool one later. Given the farm's station, the advisory times them by growing degree-days instead (thermal.py, Advisor.clocked), each crop's base and upper thresholds from the revision's Table 6.10: for the 10 crops with field-observed degree-days (its Table 6.11) each stage ends when they are gathered; for the other ten the 1998 days are taken as right for the farm's normal year (climate in the config). Each decision says how far through its curve the crop is (crop_gdd, or crop_days) beside the calendar. On the thirty summers it moved a single season's water by up to an eighth, toward what the virtual crop used, and left no crop short either way.

Plantings at different stages#

A bed often holds plants at different stages side by side: a succession, a replant in a gap, lettuce under tomatoes. A bed can name each (Bed.plantings), each on its own clock:

Water tunnel bed 3 today: its shallowest roots, in the top 15 cm, have used 39% of the water they can reach. About 20 mm brings the bed back to field capacity, down to its deepest roots at 50 cm.

What the backtests showed (record): told the tomatoes alone, lettuce sown among them went short in most summers, by the weather and by probes; told both, by the weather never. Two plantings this far apart in age are still better on two valves.

Roots that stop#

The crop's curve is for a soil that lets roots go. A hardpan, compacted fill, rock or a raised bed's floor can stop them sooner, and advice that follows the curve then counts water the roots cannot reach. How deep roots run, and what shows it, is in the root-depth note. Two ways to give the advice the right depth:

What the backtests showed (record): over a pan that passes water slowly, the rooting limit set by hand left the crop short least, on less water. With roots densest at the top, as on the simulator since 2026-10-16, a probe at 30 cm is drawn on lightly and reads near level with no pan at all, and the root signs see a stop on about half of a season's mornings with or without one; whether they are worth their place is not known (UNTESTED).

Hot days, and beds under cover#

On a hot day the refill point stays where it is by default. FAO-56's lower p for a bed drying faster than 5 mm a day (Rev.1 Eq. 8.5) is an option (Bed.adjust_p), by probes alone, the crop's use read from their drying on a day the soil was not draining (CROP_DRYING_BELOW): read from any day's, a wet summer's drainage passed for use, and six McAllister tomato summers took twice the water. At Davis it cut tomatoes' lost transpiration by 1.8 mm a summer and tomatoes over a dense pan's by 4.3, lettuce's not at all (what the trials show). Its smaller waterings are harder for the watering check to tell from none: in the 2012 drought one that arrived, over a dry top, was said not to have.

A bed under cover. Rain forecast for the farm does not reach a bed under a tunnel or a roof, and "rain is coming: wait" is the wrong advice for it. Bed.rain_share says how much of the rain reaches the bed, 0 under cover; the forecast that holds a watering, and the weather balance's rain, are scaled by it (rain_reaching_mm). Left unset, it is all of the rain, unless the bed's probes show it covered: through the last three storms of 15 mm or more in the farm's rain gauge that fell while its valve stayed shut, none moved a probe in its root zone by 0.03 within 12 hours of the rain's end (covered_by_its_probes). Only a bed whose valve reports its waterings is judged. Told, a covered bed was never held for rain that could not reach it; learning took the first part of each summer to see three storms; an open bed was never taken for covered.

Water below field capacity#

The virtual farm's soil moves water below field capacity, since 2026-10-15: each horizon holds its water along van Genuchten's curve, fitted through its own saturation, field capacity at 33 kPa and wilting point at 1500 kPa with Rosetta3's residual water, and passes it by Darcy's law at Mualem's conductivity (physics.Horizon). Water held near capacity at the top reaches the bottom of the roots on its own, and water below the roots rises to them. Checked against HYDRUS-1D on the same soils (soil models), over three days without roots; not over a season with them. Above field capacity it drains by a simpler rule than Mualem's, and a soil at field capacity drains no further, so the soil below the roots stays a store at field capacity all summer (UNTESTED). Before it, the soil moved no water below field capacity, and several early results were that soil's (record).

The crop's water use and the soil's surface#

The virtual farm's crop, since 2026-10-16: it uses its whole demand until it has used its own p of its water, then less in a line to the wilting point (FAO-56's Ks), layer by layer, weighed by its roots, densest at the top (Prasad 1988), since 2026-10-17; the top 10 cm of its soil evaporates apart from it by FAO-56's dual coefficient, a mown lawn covering 0.95 of its ground. Since 2026-10-18 its roots in wetter soil make up for those in drier, as Jarvis (1989) and HYDRUS have it, each farm's crop as far as a strength drawn for it between much and none lets it (LayeredBed.compensation): before, a dry top layer cut the whole crop's use, however wet the soil below. Its p follows the day's demand, as FAO-56 lowers it on a day the crop draws fast (p + 0.04 (5 − ETc); tomatoes' 0.4 is 0.28 at Davis's 8 mm a day). Its evaporation is spread through the top 10 cm and stops above the soil's residual water, where it took the top 25 mm first and dried it past the curve. A rain day keeps its reference ET whole, where it had lost about a sixth. At USCRN's Crossville station it dried a soil under grass at 0.74 to 1.26 of the rate the probes saw at 5 to 20 cm (soil models). Every crop takes about twice the water it did on the crop before, which slowed as soon as its soil was below field capacity; what that showed, and the advice changed for it, is in the record.

By the weather, with no probe#

A bed with no probe, given the farm's own station (eto.Station: its site and the series it keeps), is advised by FAO-56's daily root-zone balance instead (weather_balance):

Water bed 3 today, by the farm's weather: about 50% of the water its plants can use is gone, with no probe to confirm it. About 27 mm brings it back to field capacity.

When a bed's probes go silent or untrusted, it is carried on the weather from their last good readings, the mean of each probe's good ones in the three hours before it fell silent (ANCHOR_HOURS), for up to sixty days, and every advice says to check the probes:

bed 3's probes have given no reading to trust for 81 hours. By the farm's weather since their last good one, about 55% of the water its plants can use is gone: water it today, about 30 mm. And check the probes.

What the backtests showed (record): on the farm as written, whose crop used about a fifth more than the balance counted, the bed with no margin was short in 21 summers of 30, and with the fifth, in none; where the balance was not low, the margin cost a sixth to a quarter more water. Through the 2012 drought with its probe silent for two weeks, the bed carried on the weather was never short.

A probe nearby corrects the balance#

A probe lends the balance only what carries from bed to bed: its rate, not its level. A bed with no probe can name a bed nearby, of the same crop on the same station, as its reference. Each morning reference_factor reads that bed's probes over the last 30 days, on the days with no rain on the gauge, no watering, a start no wetter than field capacity and less than the readily available water gone: how much the water missing below field capacity grew, midnight to midnight, against the balance's Kc · ETo. The ratio corrects the bed's crop coefficient all the way when the probe sees it drying faster, half way when slower, because more water is the safe error, and never below the margin: a rate read truly does not make a balance stepped once a day safe on its own. It is not used with fewer than five such days, until every probe in the reference has learned its own field capacity, or below 0.5; above 1.5 it is taken at 1.5. The same rate, read from a bed's own probes before they fell silent, carries the fallback above.

bed 3 is due water by the farm's weather, with its water use set at 125% of the weather's estimate from a probe in a bed nearby: about 19 mm. Its watering is not recorded, so this takes the last advice to water as followed; if it was not, it needs more.

A soak test: field capacity on the day#

Learning a probe's field capacity takes three soakings it drains from; the soak test makes one on purpose, and checks the probes on the way (regimens/soak_test.star). A person starts it for a bed:

  1. It asks for enough water to wet the root zone 0.05 past its field capacity, over a square metre centred on the probes, where a millimetre is a litre:

    Soak test for bed 3: pour 39 litres of water over a square metre centred on its probes, a can at a time, letting each soak in before the next. The probes are checked in 3 hours, and the result comes two days later.

  2. Three hours on, the probes must have risen by half what that water should add, and read no more water than a mineral soil can hold (0.55). Either failing is said: a probe out of contact, water run off, or a probe reading high.

  3. Two days on, the level the root zone drained back to, fitted as any drain-down is, is reported: the bed's field capacity, measured. Its drain-down also counts toward the probes' learning.

Read-only like everything here: it asks a person, and checks what the probes saw.

Did the water arrive?#

The first regimen (Q10; modules/rules): regimens/watering_check.star, a few lines of Starlark. When the advice is "water today", it waits for the watering to happen and soak in, then checks that the root zone rose by at least half of what that watering should add. If not, it says so, and says both things a probe cannot tell apart:

bed 3's probes show almost no new water since this morning: the root zone rose 0.4 points, where 18 mm of watering should add about 6.0. If it was watered, check the drip line and its emitters; if it was not, it still needs water.

With the valve reporting, it says which. A bed can name the series of the valve that waters it (Bed.valve, the valve_open term). An OpenSprinkler controller reports each station opening and shutting over MQTT, and the bridge puts that in the log (sdk/mqtt_bridge.py):

bed 3's valve ran 60 minutes this morning, but its probes show almost no new water: the root zone rose 0.4 points, where 18 mm should add about 6.0. Check the drip line and its emitters for a blockage, and the line for a leak.

bed 3 was due 18 mm of water this morning, and its valve did not open. It still needs water.

What the backtests showed (record): on McAllister's 2013 and 2012 with waterings blocked on purpose, every blocked one was caught; on 2012 one watering of 61 that did arrive rose only 0.47 of its expected rise and was wrongly flagged. A bed that soaks its water up slowly, a probe set deep, or a crop that draws hard in the hours after a watering can fall below half on a day the water did arrive. The share is a parameter, and a season of a real bed's waterings is what would set it.

A probe out of the soil#

A probe lifted by a mower or a frost, or pushed in beside an air gap, reads the air: almost no water. Every health check passes it, since the reading is in range and still moves, and the advice would read the driest bed on the farm and ask for a full refill every morning.

Nothing in soil reads that dry. Below the surface only roots dry the soil, and they stop at its wilting point. The sun dries the top further, and FAO-56 takes that layer, the top 0.10 to 0.15 m, to dry "halfway between oven dry (no water left) and wilting point" (Rev.1 Eq. 7.5, the 1998 edition's Eq. 73). So a reading more than 0.03 below half the wilting point within the top 15 cm, or below the wilting point under them (soil_floor), 0.03 being a factory calibration's usual error, is not read. With no other probe the advice says why, and with the farm's station the bed is carried on the weather from the last reading that was in the soil:

bed 3's probe at 20 cm reads 2.0% water, drier than its soil can be (9.0% at least): it may be out of the soil, or in an air gap beside it. There is no advice for bed 3 today. Push the probe back in and firm the soil around it.

With another probe still in the soil, the advice comes from that one, and a note beside it says the same about the first.

the bed's soil (wilting point) the floor, top 15 cm the floor, below
silt loam (0.12) 3% 9%
clay (0.25) 9.5% 22%
sand (0.05) none: below zero 2%

So it can tell a probe in the air from dry soil only where the soil holds some water at its wilting point. In sand it cannot, and says nothing. That is the case of the openSenseMap LSE01 reading 2.1 to 2.4% (notes/test-data-sources): out of the soil in a loam, possible in a dry sand, and its keeper has not said which.

As its own process (§8.3, Q11), holding only the grant its module.json allows: python3 -I soilwater.py --socket /run/fieldmesh/modules.sock --config beds.json, the token from a systemd credential or FIELDMESH_MODULE_TOKEN (see deploy/systemd/). Through the socket it decides exactly as it does in-process; the tests run it both ways.

Code AGPL-3.0-or-later (LICENSE); strings in packs/ under the soilwater. prefix, the Spanish an unreviewed draft. It imports layer zero and layer 1.

A bed can also record what covers its soil (surface: bare, organic or inorganic mulch, plastic, turf) and what grows in it (planting). Each decision carries them, so a season's advice can be read against what the bed was. The probes see what mulch does to drying; this records why.

Not yet: