A template sensor in Home Assistant is a sensor whose value is calculated from other sensors and data, not read directly from a device. Vapor pressure deficit derived from temperature and humidity, daily light integral accumulated from a PAR sensor over the course of the day, dew point calculated from air conditions, a rolling fifteen-minute average of CO2 readings, growing degree days accumulated through the season — these are all template sensors. For agricultural operations, templates turn raw measurements into the values that actually drive decisions. A humidity sensor tells the grower the air is moist; the VPD calculation from that humidity plus temperature tells the grower whether the plants are transpiring effectively. The raw data is useful; the derived value is often what the grower watches. This page covers the Jinja2 template language in Home Assistant, the state-based and trigger-based template patterns, the specific agricultural calculations worth implementing — VPD, DLI, dew point, delta-T, growing degree days, rolling averages — the performance considerations that matter as the template library grows, and the failure modes that produce wrong-but-plausible values.
Before building templates.
Prerequisites from earlier pages:
Source sensors in place. A template that calculates VPD needs a temperature sensor and a humidity sensor to calculate from. A DLI template needs a PAR sensor. The source entities have to exist first, under known names, producing numeric values in known units. The [Your First Sensor](/home-assistant/getting-started/first-sensor) page covers adding a first sensor; the [ESPHome](/home-assistant/integrations/esphome) page covers building custom sensors.
Areas and naming conventions. Per [Organizing Home Assistant for a Farm](/home-assistant/agriculture/organizing). Template sensors need clear names that match the convention. `sensor.greenhousezone1vpd` is meaningful; `sensor.templatesensor_1` is not.
Measurement theory comfort. [Understanding Sensors](/fundamentals/understanding-sensors) covers the units and measurement principles behind the values templates calculate. A grower building a VPD template who does not understand what VPD is, or why it matters, will not know when the template is producing nonsense. Templates amplify both correct and incorrect inputs.
Some Jinja2 tolerance. Templates are Jinja2 expressions. Most agricultural calculations use only the basic subset — arithmetic, a few math functions, conditionals. Reading the examples on this page and adjusting them is enough to get productive. Deep Jinja2 mastery is not needed; deep Jinja2 mastery is also not harmful.
What a template sensor is.
A template sensor is a sensor entity whose state is the output of a Jinja2 template rather than a direct measurement. Home Assistant evaluates the template against current entity states (and other data) and sets the sensor's state to the result.
The shape of a template sensor. A template sensor has a name, a unit of measurement, a device class (optional, enables better dashboard rendering), and the template itself. Example in plain English: "Sensor named Greenhouse Zone 1 VPD, reports kPa, calculated from the temperature and humidity sensors in Greenhouse Zone 1, formula below."
Why template sensors matter in agriculture. Raw sensor data is rarely the most useful view. Temperature and humidity separately are less actionable than VPD together. PPFD instantaneously is less useful than accumulated DLI for the day. The raw data has to be present; template sensors turn it into the values the grower actually watches and the automations actually trigger on.
Template sensors are entities. They appear in the entity registry, on dashboards, in automations, in history graphs, in logbook entries, in the mobile app. Any place a sensor can be used, a template sensor can be used. A VPD template sensor is functionally a VPD sensor — Home Assistant does not distinguish.
Templates are computed when inputs change. For a standard (state-based) template sensor, Home Assistant re-evaluates the template whenever any entity referenced in the template changes state. A VPD template referencing temperature and humidity updates when either source updates. This is efficient for most uses but has implications for templates that reference many entities or that include time-based functions, discussed later under performance.
Jinja2 in Home Assistant.
Jinja2 is a template language. In Home Assistant, Jinja2 expressions live between double curly braces: `{{ ... }}`. The expression is evaluated and its result becomes the template's output.
Basic arithmetic and functions.
The common operations:
- Arithmetic: `{{ 2 + 3 }}`, `{{ 10 / 3 }}`, `{{ 7 * 2 }}`. - Math functions: `abs()`, `min()`, `max()`, `round()`, `sqrt()`, `log()`, `exp()`. - Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`. - Conditional (ternary): `{{ "hot" if temp > 80 else "cool" }}`.
Accessing entity states.
The primary function is `states()`. Useful variants:
- `states('sensor.greenhousetemp')` — returns the current state as a string. - `states('sensor.greenhousetemp') | float(0)` — converts to a float, with a default of 0 if conversion fails. The default is important; sensors can be unavailable, and `float` without a default on a non-numeric value produces template errors. - `stateattr('sensor.greenhousetemp', 'unitofmeasurement')` — returns a specific attribute of the entity. - `is_state('switch.pump', 'on')` — returns true or false for a state check.
Filters.
Jinja2's pipe operator applies filters to values. Useful ones in Home Assistant:
- `| float(default)` — convert to float with a default. - `| int(default)` — convert to integer with a default. - `| round(digits)` — round to the specified number of decimal places. - `| default(value)` — provide a fallback when the value is undefined.
Conditionals.
Full if/else blocks work in Jinja2 beyond the inline ternary:
``` {% if states('sensor.greenhousetemp') | float(0) > 85 %} hot {% elif states('sensor.greenhousetemp') | float(0) > 70 %} warm {% else %} cool {% endif %} ```
Blocks like this appear less often in sensor templates (which usually evaluate to a single value) and more often in notification message templates.
Testing templates.
Home Assistant provides a template editor in Developer Tools → Template. Paste a template there to see its current value against current entity states. The template editor shows errors, type information, and live updates as source entities change. It is the fastest way to develop a template — get it working in the editor, then move it to a sensor configuration.
State-based versus trigger-based templates.
Home Assistant supports two kinds of template sensors, with different behaviors.
State-based template sensors. The default. The sensor re-evaluates its template whenever any entity referenced in the template changes. A VPD template referencing temperature and humidity updates when either source updates. State-based templates are simple, efficient for most uses, and appropriate for derived values.
Trigger-based template sensors. The sensor updates only in response to specified triggers. The sensor's value does not automatically change when referenced entities change — only when a trigger fires. Trigger-based templates are useful when:
- The template needs to run on a schedule (every five minutes, say) regardless of when source entities update. - The calculation is expensive and should not run on every source update. - The value should snapshot at specific moments rather than continuously track inputs. - The sensor should depend on time or on events that are not entity state changes.
A trigger-based template sensor can use any of the standard automation triggers — time, time pattern, template, state, platform-specific. The sensor's state updates only when one of those triggers fires.
When to pick which. Most agricultural calculations are correctly state-based: VPD, dew point, delta-T, simple derived metrics. They should track inputs in near-real-time. Some calculations are correctly trigger-based: a DLI reset at start-of-day, a daily average that snapshots once per day, a rolling window that updates on a schedule rather than on every source tick. When in doubt, state-based is usually right.
VPD calculation.
Vapor pressure deficit is one of the most useful derived metrics in greenhouse and indoor growing. It captures the difference between how much moisture the air is holding and how much it could hold at the current temperature — the drying power of the air as plants experience it.
The formula.
VPD in kilopascals, from air temperature and relative humidity:
SVP (kPa) = 0.6108 × exp((17.27 × T) / (T + 237.3))
VPD (kPa) = SVP × (1 − RH / 100)
Where T is air temperature in °C and RH is relative humidity as a percentage (0–100). The formula uses the Magnus-Tetens approximation, which is accurate enough for horticultural purposes across normal growing temperatures.
A worked example.
At 24°C and 65% relative humidity:
SVP = 0.6108 × exp((17.27 × 24) / (24 + 237.3)) SVP = 0.6108 × exp(414.48 / 261.3) SVP = 0.6108 × exp(1.586) SVP = 0.6108 × 4.884 SVP = 2.98 kPa
VPD = 2.98 × (1 − 65 / 100) VPD = 2.98 × 0.35 VPD = 1.04 kPa
A VPD of about 1.0 kPa is in the middle of the typical desirable range for vegetative growth of many crops, so the worked example produces a reasonable number. When the template produces numbers that do not make sense — negative values, numbers far outside the expected 0.4 to 1.6 kPa range — the inputs are the first place to check.
Air VPD versus leaf VPD.
The calculation above is air VPD — the drying power of the air using air temperature. Leaf VPD uses leaf temperature (usually a few degrees cooler than air for a transpiring crop) and captures what the plant's surface actually experiences. For most hobby and small-commercial operations, air VPD is sufficient and is what most published VPD targets refer to. For operations with infrared leaf temperature sensors or tight environmental control on high-value crops, leaf VPD is more precise.
Leaf VPD:
SVP(Tleaf) (kPa) = 0.6108 × exp((17.27 × Tleaf) / (T_leaf + 237.3))
VPDleaf (kPa) = SVP(Tleaf) − (SVP(T_air) × RH / 100)
Implementing VPD as a template sensor.
VPD targets for common crops.
Rough VPD target ranges (crop-specific and stage-specific; consult variety guides for production operations):
- Seedlings and propagation: 0.4 to 0.8 kPa - Vegetative lettuce and greens: 0.8 to 1.2 kPa - Vegetative tomatoes, peppers, cucumbers: 0.8 to 1.2 kPa - Fruiting tomatoes: 1.0 to 1.4 kPa - Flowering cannabis: 1.2 to 1.6 kPa
These are starting points. Operations with specific crop expertise tune these within their own programs.
DLI calculation.
Daily light integral is the total photosynthetically active light a crop receives over the course of a day, measured in moles per square meter per day (mol/m²/d). DLI drives growth; tracking it turns a PAR sensor's instantaneous reading into a number that actually connects to yield.
The formula.
DLI (mol/m²/d) = integral of PPFD (μmol/m²/s) over the photoperiod (seconds), divided by 1,000,000.
For the special case of constant PPFD:
DLI = PPFD × photoperiod (seconds) / 1,000,000
At 500 μmol/m²/s over a 12-hour photoperiod (43,200 seconds):
DLI = 500 × 43,200 / 1,000,000 = 21.6 mol/m²/d
Real DLI is rarely constant PPFD over the day — it follows a solar curve in greenhouses, or a mostly-flat profile in indoor operations with possible ramps at the start and end of the photoperiod. The integral is calculated from actual PAR sensor readings over time.
Implementing DLI.
DLI is a classic trigger-based pattern in Home Assistant. The approach:
1. Use the Integration platform (Riemann sum integrator) to integrate PPFD over time. This produces a running total in μmol/m² that accumulates from the start of the integration period. 2. Divide the running total by 1,000,000 to convert to mol/m² (the DLI units). 3. Reset the integrator at the start of each day (or at the start of the photoperiod, if preferred).
Once DLI is tracked as a sensor, it becomes available for automations: notify when the DLI target is met, shut off supplemental lighting when the target is reached, summarize the day's DLI at end-of-photoperiod. [Lighting Control](/home-assistant/agriculture/lighting) covers the automation patterns that use DLI.
DLI reset timing.
The most common reset is at local midnight. For operations where the photoperiod does not cross midnight, this is fine. For operations with photoperiods that do cross midnight (some indoor flowering schedules), resetting at the start of the photoperiod makes more sense — otherwise the DLI for the night portion of the "day" gets cut off at midnight and restarted, producing confusing numbers.
DLI targets for common crops.
Starting points (tune per variety and stage):
- Lettuce and greens: 12 to 17 mol/m²/d - Herbs: 10 to 15 mol/m²/d - Tomatoes, peppers: 22 to 30 mol/m²/d - Strawberries: 17 to 22 mol/m²/d - Cannabis vegetative: 20 to 35 mol/m²/d - Cannabis flowering: 35 to 55 mol/m²/d - Seedlings and propagation: 8 to 12 mol/m²/d
Dew point and other psychrometric derivations.
Dew point is the temperature at which the air would become saturated if cooled at constant pressure. It is useful for anticipating condensation on plant surfaces (disease pressure) and for identifying when structural surfaces will get wet. A greenhouse where the dew point is approaching the leaf temperature is a greenhouse about to grow pathogens.
The formula.
Magnus-Tetens dew point approximation, given air temperature T (°C) and relative humidity RH (%):
α = ((17.27 × T) / (237.3 + T)) + ln(RH / 100)
T_dew (°C) = (237.3 × α) / (17.27 − α)
A worked example.
At 22°C and 80% RH:
α = ((17.27 × 22) / (237.3 + 22)) + ln(0.80) α = (379.94 / 259.3) + (−0.223) α = 1.465 + (−0.223) α = 1.242
Tdew = (237.3 × 1.242) / (17.27 − 1.242) Tdew = 294.72 / 16.028 T_dew ≈ 18.4°C
So at 22°C and 80% RH, the dew point is about 18.4°C — meaning any surface cooler than 18.4°C starts collecting condensation.
Implementing dew point as a template sensor.
Other useful psychrometric values.
- Absolute humidity (grams of water per cubic meter of air) — tells the grower how much water is actually in the air, independent of temperature. Useful for dehumidification calculations. - Humidity ratio (grams of water per kilogram of dry air) — similar purpose, in mass terms. - Wet-bulb temperature — the temperature a thermometer wrapped in a wet cloth would read. Used in swamp cooling calculations and in some spray-application decisions.
Each can be computed from temperature and humidity with similar Magnus-Tetens-based formulas. The [Climate Control Patterns](/home-assistant/agriculture/climate-control) page covers when each is useful.
Growing degree days.
Growing degree days (GDD) accumulate the effective heat a crop experiences above a base temperature. GDD is used to predict phenological events — when a crop will flower, when pests will reach specific life stages, when harvest readiness is approaching.
The formula.
The simplest method:
GDD (day) = ((Tmax + Tmin) / 2) − T_base
If the calculated daily GDD is less than zero, it is treated as zero. Accumulated GDD is the sum of daily GDD over the growing period.
Base temperatures.
Base temperatures are crop-specific. Common values (°F and °C):
- Corn (field): 50°F / 10°C - Wheat: 40°F / 4.4°C - Tomato: 50°F / 10°C - Potato: 45°F / 7.2°C - Strawberry: 40°F / 4.4°C - Many insect pests: 50°F / 10°C (check specific pest guides)
Some crops use upper thresholds as well — temperature above the upper threshold does not add further heat units. Modified GDD formulas handle these caps.
Implementing GDD as a template sensor.
GDD is a good candidate for a trigger-based template sensor. It does not need to update as inputs change; it needs to update once per day, based on that day's max and min temperatures.
For ornamental and vegetable operations, GDD tracking is more useful for multi-season comparison than for day-to-day decisions. For commodity crops and for pest management (where GDD models predict insect emergence reliably), it is a daily operational tool.
Rolling averages and trends.
Raw sensor values are noisy. A 30-second average of CO2 readings is steadier than the instantaneous reading. A 15-minute average of soil moisture smooths out short-term noise that should not drive decisions.
Statistics sensor.
Home Assistant's Statistics integration produces rolling statistics — mean, median, min, max, standard deviation, rate of change — over a configurable window. A 15-minute rolling average of CO2 is a Statistics sensor with `samplingsize` or `maxage` set to 15 minutes and `state_characteristic: mean`.
Filter sensor.
For simpler smoothing — a low-pass filter, a time-throttled sensor, an outlier-rejection filter — the Filter integration is lighter than full Statistics. A `timesimplemoving_average` filter with a 5-minute window produces a smoothed output without the overhead of full statistics.
Template sensors for derivatives and trends.
A template sensor can compute the rate of change by referencing the current state and a recent-history state via `states()` or `history` templates. This is more advanced and the Statistics platform's `change_second` characteristic is often easier. For operations wanting custom derivative logic (rate of change over a specific non-standard window, weighted rate of change), a trigger-based template sensor can calculate whatever is needed.
Smoothing versus responsiveness.
A heavy smoothing window (an hour of averaging) produces steady readings but lags real changes. A light smoothing window (a minute of averaging) stays responsive but is noisy. Agricultural decisions usually want something in the middle — 5 to 15 minutes of smoothing for slow-moving variables like temperature and humidity, 30 seconds to a minute for fast-changing variables like light in a partly-cloudy greenhouse.
Template attributes.
A template sensor's state is one value. Template attributes let the sensor carry additional data alongside its state.
When attributes are useful.
Attributes are useful for:
- Carrying metadata: "last updated at," "source sensor," "calculation method." - Exposing intermediate values: a VPD sensor might expose the calculated SVP as an attribute for debugging. - Grouping related values: a zone-conditions sensor might report its primary metric as state and carry all related conditions as attributes.
How attributes appear.
Attributes are visible in Developer Tools → States for the entity, accessible in automations via `state_attr()`, and displayable on dashboards through specific cards (or through template cards). They are not history-graphed by default (only the state is), so primary metrics should be the state, not an attribute.
When not to use attributes.
Individual values that each need independent history graphing, independent alerting, or independent dashboard display are better as separate sensors. If the grower wants to alert on VPD specifically, VPD should be a sensor's state, not an attribute of a combined sensor.
Availability and error handling.
Template sensors can produce errors when their source entities are unavailable or their math breaks (divide by zero, log of a negative number, invalid inputs). Handling these cases cleanly prevents cascading failures.
Unavailability propagation.
A template that references an unavailable sensor can be made to report unavailable itself, using `availability` templates. The alternative — reporting zero, or the last value, or a default — may produce misleading automation behavior. A VPD template that reports 0 kPa because the humidity sensor is unavailable looks like dangerously dry air and could trigger cooling.
Default values with float and int filters.
The pattern `states('sensor.temp') | float(0)` is convenient but silent. If the sensor is unavailable, the template produces 0 without any indication. For derived values, this often produces nonsense that propagates into automations. The better pattern, in many cases, is to make the template sensor report unavailable when its inputs are unavailable, using the `availability` field:
``` availability: > {{ hasvalue('sensor.greenhousetemp') and hasvalue('sensor.greenhousehumidity') }} ```
A VPD sensor that is unavailable when its source sensors are unavailable is easier to debug than a VPD sensor that reports a wrong-but-plausible value.
Math errors.
Templates with operations that can produce math errors (log of a non-positive number, divide by zero) should guard against the error conditions. A check for positive RH before calculating dew point, a check for non-zero denominator before dividing. Guard failures can return a sentinel value or, better, make the sensor unavailable through the availability template.
Performance considerations.
Most template libraries are small enough that performance does not matter. Some grow large enough to matter.
Templates re-evaluate on input changes. A state-based template re-evaluates whenever any referenced entity changes. A template referencing five sensors that each update every 10 seconds re-evaluates roughly 30 times per minute. This is fine. A template referencing 50 sensors that each update every second re-evaluates 3,000 times per minute, which starts consuming real CPU.
Triggers help for expensive templates. A complex calculation that does not need to update on every source tick should be trigger-based. A daily summary sensor that aggregates across 20 zones does not need to re-run every time any zone's temperature changes — it can run once per hour or once per day.
`this` is cheaper than repeated template evaluation. Inside a template, repeated references to the same `states('...')` call re-evaluate each time. Using a Jinja2 `set` to capture a value once and referring to it multiple times is cheaper:
``` {% set t = states('sensor.temp') | float(0) %} {% set rh = states('sensor.humidity') | float(0) %} {{ 0.6108 (2.718 ((17.27 t) / (t + 237.3))) * (1 - rh / 100) }} ```
For a single formula this is a micro-optimization. For complex templates referencing the same entities many times, it is real.
Avoid templates that reference all entities. A template that uses `states | list` to iterate over every entity in Home Assistant evaluates against thousands of entities on every change. This almost never needs to happen; most such templates can be narrowed to a specific domain or area.
Common failure modes.
Specific template-sensor problems from real deployments.
The template that reported zero for a week. A VPD template referenced `sensor.greenhousehumidity` which had been renamed to `sensor.greenhousezone1humidity` during a reorganization. The template's `| float(0)` default caused it to return 0 silently when the entity was missing. The grower thought VPD was just really low. Fix: use `has_value()` or the `availability` template field to make the sensor unavailable when inputs are missing rather than returning a default.
The VPD that went negative. The humidity sensor returned 105% briefly during a condensation event; the VPD calculation produced a small negative number; the automation that triggered on "VPD below 0.5" fired when VPD should have been impossible. Fix: clamp inputs to reasonable ranges (RH to 0–100) in the template; test the calculation against edge inputs.
The dew point that reported NaN. A template's `ln(RH / 100)` failed when humidity briefly read 0. Home Assistant returned a template error. The dew point sensor went unavailable. Automations referencing it received unavailable states. Fix: guard the log operation (`if RH > 0 else some fallback`), or use availability templates to skip evaluation when inputs are out of valid range.
The DLI that reset mid-photoperiod. A utility_meter was configured to reset daily at midnight, but the operation's photoperiod ran from 6 AM to midnight on one schedule and from 8 PM to 8 AM on another. The midnight reset chopped one operation's DLI in half every day. Fix: choose the reset time based on the photoperiod; a daily reset at start-of-photoperiod (or at a consistent time that falls outside the photoperiod) aligns with operational reality.
The rolling average that lagged behind. A 30-minute rolling average of CO2 was used as the input to a ventilation automation; by the time the average crossed the threshold, the actual CO2 had been high for 20 minutes. Fix: match the smoothing window to the responsiveness needed. For fast-response controls, a shorter window or an unsmoothed input with adequate hysteresis is better.
The template that worked perfectly in the editor and wrong in production. A template tested in Developer Tools → Template used entity states that had just updated. When deployed, the template sometimes evaluated against stale states during startup, producing wrong initial values until inputs refreshed. Fix: test templates including the startup window; expect the first few minutes after a restart to produce unstable values.
The template whose formula was wrong. A VPD template used SVP × RH / 100 instead of SVP × (1 − RH / 100). The sensor reported values that looked like VPD but were actually vapor pressure. The automations based on it drove the operation in the wrong direction. Fix: verify calculations against known test cases before relying on derived sensors. Published VPD at 24°C and 65% RH should be about 1.04 kPa; if the sensor reports something else, the formula is wrong.
The GDD that reset at the wrong time. A GDD accumulator reset at midnight local time. A daylight-saving-time transition caused the automation to fire twice on the spring-forward day and to miss firing entirely on the fall-back day (depending on the direction and logic). Fix: use Home Assistant's native time handling, which handles DST correctly, and test around transition dates.
The template that slowed the system. A monitoring template referenced `states | list` to count all entities in Home Assistant. With several thousand entities, the template re-evaluated on every state change — effectively continuously — consuming measurable CPU. Fix: narrow the scope of entity-listing templates (filter by domain or area), or make such templates trigger-based so they run on a schedule rather than on every change.
What not to do.
Patterns to avoid.
Don't swallow errors with silent defaults. `| float(0)` is convenient but dangerous for derived values. A sensor silently reporting zero because its source was unavailable is worse than a sensor reporting unavailable. The availability template is the right tool when inputs may be unavailable.
Don't skip unit checks. Home Assistant does not enforce that a template sensor's declared unit matches what the template actually produces. A VPD template that reports kPa in its declaration but produces Pa from its math is a disaster waiting to happen. Verify units against worked examples.
Don't put all derived values in one huge template sensor with attributes. Each derived value the grower wants to alert on, graph over time, or display on a dashboard should be its own sensor. Attributes are for metadata, not for primary metrics.
Don't over-smooth. A 4-hour rolling average of temperature feels steady on a dashboard but lags real changes by hours. For control-relevant values, match smoothing to the responsiveness the operation actually needs.
Don't assume templates update when you want them to. A state-based template only re-evaluates when a referenced entity changes. If the template includes a time function (`now()`) but does not reference any entity that updates frequently, the time portion of the template can be stale. Either trigger-based updates or a time-updating entity reference fix this.
Don't skip testing against edge values. 0% humidity, 100% humidity, very cold and very hot temperatures, the moment a sensor is unavailable, the moment after Home Assistant starts. Edge cases reveal the template bugs that average cases hide.
Don't use template sensors as general-purpose scripting. A template sensor is a sensor. Scripts and automations are for procedural logic. A template sensor that implements a complex state machine is in the wrong place.
Don't ignore performance warnings in the Home Assistant logs. Home Assistant sometimes logs warnings about templates that re-evaluate excessively or take unusually long. These warnings get easier to ignore and harder to debug over time. Addressing them early is cheaper than debugging mysterious slowdowns later.