Your monitoring data is what decides when the panels get cleaned, not the calendar. The trigger is a clear day that generates measurably less than your clean baseline — the yield your array produced on the first properly sunny day after rain had rinsed the glass. Until a day actually beats that comparison, whatever is sitting up there is cosmetic and can wait.
I check mine from the patio most mornings, phone leaning against the coffee mug, watching the yield curve climb out of the early ramp. Quarter past nine is usually when the day shows its hand. If the actual line is hugging the expected line the way it did on that post-rain morning, I close the app and get on with the day. When it is running low and the sky is that hard, cloudless blue you get on the Highveld after a storm front has moved through, then I start asking what changed.
Rain hands you a free baseline
The thing most people never notice is that a good downpour cleans the glass for you. Not perfectly — runoff leaves its own marks along the frame edges — but well enough that the first clear day afterwards is the cleanest your array is going to be for months.
So that is the day you screenshot. Same array, same roof, same season, panels as clean as they get without a ladder. From then on, every clear day gets measured against it.
What you are looking for is not a number you can memorise. It is whether the actual yield curve sits on top of the expected curve for that day, or under it. Most monitoring platforms draw both: one line from the inverter's real output, one from an irradiance model. On a genuinely clean, genuinely cloudless day, those two should run close together through the middle of the day.
When they don't, and the sky was clear, and nothing about the day was different from the baseline day, soiling becomes a credible explanation worth investigating. Worth investigating, not automatically proven. That distinction matters more than anything else in this piece.
Read the shape of the day, not the monthly total
A monthly total tells you almost nothing. Too many things move inside a month.
The daily curve, though, has a shape, and the shape is diagnostic. A broad, even suppression across the whole day — the peak lower, the shoulders lower, the whole thing slightly flattened — points at something distributed across the array. Dust, pollen, a fine film of agricultural or industrial fallout, salt on a coastal roof. Nothing sitting in one spot.
A notch is different. A notch is local.
If the curve dips hard for twenty or thirty minutes and then recovers, or if the day's total is down but the shape is otherwise normal, you are probably looking at one or two panels with something on them. Bird droppings are the classic. Hadedas and pigeons favour the same panels repeatedly, and once a dropping sets and bakes, it does not wash off with the next light shower. Leaf litter matters too — jacaranda and plane tree leaves that land and stick, gum leaves, pine needles swept onto the bottom row by a gust.
This is where per-panel or module-level data earns its keep. If your system has optimisers or micro-inverters, or any module-level electronics reporting individually, a localised drop shows up immediately as one panel or one short run underperforming while its neighbours are fine. String-level monitoring gets you part of the way: one MPPT input dropping relative to the other tells you which half of the roof to look at, but not which panel.
If you only have the whole-system view, you can still catch it. Compare the same time window across consecutive clear days. A repeatable dip at a consistent clock time is a shadow of something physical, and shadows don't move.
The comparison trap that makes people clean for nothing
This is the mistake I made twice before I learned better. I compared March to December, saw the gap, and went up a ladder.
Sun angle and day length change through the year. They change a lot. Comparing a winter day to a summer day tells you that it is winter. It tells you nothing about soiling. A drop against a different month is not automatically a cleaning signal — it is usually just the earth doing what the earth does.
So the rule I use: only compare against a clear day within a few weeks of the day you are looking at, in the same season, on a day with similar weather. If you don't have one, go and look at the shape of the curve instead of the total. Shape survives seasonal change better than numbers do.
And if a proper rain has just come through? You get a free controlled experiment. Look at the last clear day before the rain and the first clear day after. Same season, same roof, one variable. If production lifts after the rain, the rain cleaned something. If it doesn't lift, then whatever is suppressing your output was never on the glass in the first place.
When cleaning doesn't bring the numbers back
This is the part that saves you money. Clean the array, then wait for the next equivalent clear day — clear sky, similar sun, similar season. If the yield comes back to baseline, you had a soiling problem and you've solved it.
If it doesn't come back, stop cleaning. You've just ruled out soiling for the price of a bucket of water, and that is genuinely useful information.
The usual suspects, in rough order of how often I've seen them:
Clipping. If the inverter is sitting at its maximum output for hours in the middle of the day — the curve goes flat across the top like a plateau — then during those hours there is nothing extra for clean panels to add. The inverter can't pass it through. Soiling in a clipping system shows up at the shoulders of the day, morning and late afternoon, not at noon. People clean, look at the noon peak, see no change, and conclude cleaning doesn't work.
Shading. A tree that grew. A new pole. A neighbour's extension. Shading losses often present like a soft, repeatable suppression at the same time every day rather than the random look of dirt.
Something else on the property changed. A geyser timer that now runs at midday, a pool pump on a new schedule, a battery charging earlier in the morning, extra load pulling the system harder. On some monitoring setups, a change in consumption pattern can look like a change in generation if you're reading the wrong graph. Check you're looking at production and not self-consumption.
How I actually clean, and when
I don't have a schedule. I have a threshold I can see.
When the data says go, I go early — first thing in the morning, panels still cool from the night. Washing hot glass under a Highveld sun is asking for trouble, and hard water dries into spots before you've finished the row anyway. Plain water, a soft brush on an extension pole, gentle pressure, no high-pressure washer anywhere near the frames, junction boxes or cable entries. No abrasives, no dish soap, nothing that leaves a residue.
Then I log it. Date, the yield on the clean day, and the yield on the next equivalent clear day. That log becomes my own baseline going forward, tuned to my roof, my dust, my birds, my season. Not a generic figure off a website.
What this approach can't tell you
The honest limits. Monitoring resolution varies between systems, and a platform sampling every fifteen minutes will smooth out a short dip that a five-minute sample would catch. String-level data can tell you a string is down; it can't point at a panel. Some platforms compute their expected line from a fairly optimistic clear-sky model, which makes a normal day look like a bad day — if every day looks slightly disappointing, suspect the model before you suspect the glass.
And the whole method rests on having a clean reference day. If your array has not been cleaned since installation and it has never rained properly on it, you don't have a baseline. You have an unknown. In that case, clean once, deliberately, on a day you've chosen for the purpose, and let the next clear day establish the reference you'll use for everything after.
That's the whole idea. The panels don't need a routine. They need a reason, and your own data is where the reason lives.