The first cold mornings of November bring the same question every year: the outdoor unit of a heat pump is white with frost. Is that normal, or is it broken?
The answer is a short decision list, and a decision list is easy to write as code. This post turns the checks from HVAC Fitter's heat pump iced up guide into a function you can read top to bottom. It's a toy for learning, not a repair tool.
The facts the code encodes
From the guide (updated Oct 22, 2026):
- Light white frost is normal when it's near freezing outside. Moisture sticks to the cold coil, and the defrost cycle clears it.
- Thick ice or a solid block is not normal. Airflow stops and heating drops fast.
- During defrost, the unit briefly runs in cooling mode and the outdoor fan usually stops. So a still fan during defrost is expected. A fan that won't spin while heating is not.
- A fan that hums but won't spin often points to a bad capacitor rather than a dead motor.
- Ice that stays for days points to the defrost control board.
- A clogged indoor filter can disrupt the cycle too. Most filters need changing every 1 to 3 months.
- Refrigerant work needs an EPA Section 608 certified tech. That's always the last branch.
The function
from dataclasses import dataclass @dataclass class Observation: ice: str # "none", "light frost", "thick ice" or "solid block" hours_iced: float # how long the ice has stayed outdoor_fan_spinning: bool # while the unit is heating (not during defrost) fan_hums: bool # you hear a hum but the blades don't turn filter_age_months: float # months since the indoor filter was changed def triage(o: Observation) -> list[str]: steps = [] if o.ice in ("none", "light frost"): return ["Normal. Light frost is standard near freezing; the defrost cycle clears it."] if o.filter_age_months >= 3: steps.append("Replace the indoor air filter (most need it every 1 to 3 months).") if not o.outdoor_fan_spinning: if o.fan_hums: steps.append("Fan hums but won't spin: suspect the run capacitor before the motor.") else: steps.append("Fan dead: check the motor; a replacement must match HP, volts and rotation.") if o.hours_iced >= 48: steps.append("Ice that stays for days: suspect the defrost control board.") steps.append("Still iced after that, or any refrigerant question: call a licensed tech.") return steps if __name__ == "__main__": cases = { "frosty morning": Observation("light frost", 1, True, False, 1), "old filter": Observation("thick ice", 6, True, False, 5), "humming fan": Observation("thick ice", 30, False, True, 1), "solid block": Observation("solid block", 48, True, False, 2), } for name, obs in cases.items(): print(f"{name}:") for s in triage(obs): print(" -", s) Output
frosty morning: - Normal. Light frost is standard near freezing; the defrost cycle clears it. old filter: - Replace the indoor air filter (most need it every 1 to 3 months). - Still iced after that, or any refrigerant question: call a licensed tech. humming fan: - Fan hums but won't spin: suspect the run capacitor before the motor. - Still iced after that, or any refrigerant question: call a licensed tech. solid block: - Ice that stays for days: suspect the defrost control board. - Still iced after that, or any refrigerant question: call a licensed tech. Why the order matters
The checks run cheapest-first. A filter costs a few dollars and takes a minute, so it comes before anything electrical. The "call a licensed tech" line is never skipped. Every frozen case ends there if the easy fixes don't work.
Two things the code doesn't try to do:
- It doesn't tell you to open the unit. The guide's safety rules come first: power off at the breaker and the outdoor disconnect, test for power, and remember that a capacitor can hold a charge with the power off. If that isn't you, stop and call a pro.
- It doesn't name a part. If the fan hums, the next step is reading the capacitor label and matching it. See which capacitor do I need. A replacement fan motor must match horsepower, voltage and rotation.
Try extending it
- Add a
defrost_runningflag so a still fan during defrost returns "normal". - Return objects with a
costandneeds_profield, then sort by them. - Write tests for the four cases above with
pytest.
If you'd rather check with a person than a function, the full guide has the thaw steps and the part lists: hvacfitter.com/guides/heat-pump-iced-up.