DEV Community

Cover image for Thermal Comfort for Developers: Implementing PMV/PPD Logic to Decide When a Ceiling Fan Should Run
Priyal Vijay
Priyal Vijay

Posted on

Thermal Comfort for Developers: Implementing PMV/PPD Logic to Decide When a Ceiling Fan Should Run

Most “smart” homes spin a ceiling fan when the room feels warm — usually based on a single temperature threshold. That’s crude. If you want comfort and efficiency, you should drive the fan from a thermal comfort model, not a basic if-temp-then-fan rule.

This post shows how to implement PMV/PPD-based logic (with a lightweight fallback heuristic) so your automation decides when a ceiling fan should actually run. Along the way, remember: a fan doesn’t always improve airflow or comfort in a meaningful way. Here’s a practical explanation of Will a ceiling fan improve air flow?


What Are PMV and PPD?

PMV (Predicted Mean Vote) estimates how people feel about thermal conditions on a -3 (cold) to +3 (hot) scale.

PPD (Predicted Percentage Dissatisfied) converts that into a dissatisfaction percentage.

Inputs typically include:

  • Air temperature (°C)
  • Mean radiant temperature (°C)
  • Relative humidity (%)
  • Air speed (m/s)
  • Metabolic rate (met)
  • Clothing insulation (clo)

In residential settings, reasonable defaults for unknowns:

  • met = 1.1 (sitting/standing indoors)
  • clo = 0.6 (short sleeves, light pants)

A Minimal Python Implementation of PMV/PPD

This is a lightweight Python function you can integrate into Home Assistant, Node-RED, or a custom automation stack:

`import math

def pmv_ppd(tdb, tr, vr, rh, met=1.1, clo=0.6, wme=0.0):
pa = rh * 10 * math.exp(16.6536 - 4030.183 / (tdb + 235))
icl = 0.155 * clo
m = met * 58.15
w = wme * 58.15
mw = m - w

if icl <= 0.078:
f_cl = 1 + 1.29 * icl
else:
f_cl = 1.05 + 0.645 * icl

hcf = 12.1 * math.sqrt(vr)
taa = tdb + 273
tra = tr + 273
t_cla = taa + (35.5 - tdb) / (3.5 * icl + 0.1)

p1 = icl * f_cl
p2 = p1 * 3.96
p3 = p1 * 100
p4 = p1 * taa
p5 = 308.7 - 0.028 * mw + p2 * (tra / 100) ** 4

xn = t_cla
eps = 0.00015
for _ in range(150):
xf = xn
hcn = 2.38 * abs(100 * xf - taa) ** 0.25
hc = max(hcf, hcn)
xn = (p5 + p4 * hc - p2 * (xf / 100) ** 4) / (100 + p3 * hc)
if abs(xn - xf) <= eps:
break

tcl = xn - 273
hl1 = 3.05 * 0.001 * (5733 - 6.99 * mw - pa)
hl2 = 0.42 * (mw - 58.15) if mw > 58.15 else 0
hl3 = 1.7e-5 * m * (5867 - pa)
hl4 = 0.0014 * m * (34 - tdb)
hl5 = 3.96 * f_cl * ((xn / 100) ** 4 - (tra / 100) ** 4)
hl6 = f_cl * hc * (tcl - tdb)

ts = 0.303 * math.exp(-0.036 * m) + 0.028
pmv = ts * (mw - hl1 - hl2 - hl3 - hl4 - hl5 - hl6)
ppd = 100 - 95 * math.exp(-0.03353 * pmv ** 2 - 0.2179 * pmv ** 2)

return pmv, ppd`

Enter fullscreen mode Exit fullscreen mode




Turning PMV Into a Fan Control Rule

Once you compute PMV and PPD, you can implement a decision rule like this:
def should_run_fan(pmv, rh, fan_air_speed, max_ppd=20):
if rh > 70:
return False
return pmv > 0.5 and fan_air_speed < 0.5 # 0.5 m/s ~ medium speed

In plain terms: if it feels too warm and humidity isn’t too high, run the fan before adjusting the thermostat.

Home Assistant YAML Automation Example

`alias: Fan Comfort Control
trigger:

  • platform: state entity_id:
    • sensor.living_temp
    • sensor.living_humidity
    • sensor.living_air_speed action:
  • service: python_script.compute_pmv data: temp: "{{ states('sensor.living_temp') | float }}" rh: "{{ states('sensor.living_humidity') | float }}" air_speed: "{{ states('sensor.living_air_speed') | float }}"
  • choose:
    • conditions:
      • condition: numeric_state entity_id: sensor.living_pmv above: 0.5
      • condition: numeric_state entity_id: sensor.living_rh below: 70 sequence:
      • service: fan.turn_on target: entity_id: fan.living_ceiling data: percentage: 60 default:
    • service: fan.turn_off target: entity_id: fan.living_ceiling `

Can't Use PMV? Use This Heuristic

If you don’t want to implement full PMV logic, use this pragmatic fallback:
if temp > 24.5 and 40 <= rh <= 60:
run_fan()
elif temp > 26.0 and rh < 70:
run_fan()
else:
keep_fan_off()

Be sure to add hysteresis (e.g. don't turn off unless temp < 24.0°C) and time thresholds to prevent flip-flopping.

What to Track

To verify your logic is working:

  • Energy Use: Compare HVAC runtime before and after fan-first logic
  • Comfort Score: Track average PMV or temp/RH band compliance
  • Fan Events: Log toggles to detect unnecessary oscillations

Wrap-Up

Thermal comfort is measurable. PMV/PPD gives you a smarter foundation than simple temperature triggers. And before you assume fans always help, make sure to understand when they don’t:

Will a ceiling fan improve air flow?

Good automation comes from accurate sensing and smart thresholds — not guesswork.

Top comments (0)