Every solar plant already measures itself. The inverters report power output, temperature, lifetime energy, every few seconds, in a standard protocol most of them already speak. The data is sitting right there on the wire. Most small and mid-size operators, the rooftop plants on factories, schools, hospitals, warehouses, never read it, because the platforms that would read it for them are built for utility-scale plants with utility-scale budgets.
We built Suryamon to read it anyway. Free, open source, self-hosted. And in building it, we ran into a decision that ended up defining the whole project more than any feature did.
The number everyone gives you anyway
Performance Ratio is the single most trusted metric in solar. It answers the question that actually matters: of the energy the sun offered a plant today, how much did it actually capture. A PR of 0.80 means the sun offered 100 units and the plant delivered 80. Healthy plants sit in the 0.75 to 0.85 range, and a PR that starts drifting down is usually the earliest sign of soiling, degradation, or equipment trouble, weeks before anyone would notice it any other way.
Computing PR correctly requires irradiance data, how much sunlight actually hit the panels, measured by a pyranometer. Most small operators don't have one installed. It's an extra sensor, extra cost, extra thing to maintain, and plenty of rooftop plants skip it entirely.
Here's what nearly every monitoring platform does when the pyranometer is missing: it shows you a PR number anyway. Usually built on a modeled or assumed irradiance curve for the region and season, close enough to look plausible, precise enough to look measured. The dashboard shows 0.81 with the same confidence whether that number came from a real sensor or a guess dressed up in two decimal places.
What we refused to do
Suryamon does not do this. If there's no pyranometer, there's no PR. Full stop.
What the plant gets instead is specific yield and CUF, capacity utilization factor, both of which are real, computable, honest numbers that don't require irradiance data to mean something true. Specific yield tells you energy per unit of installed capacity, the fairest way to compare plants of different sizes. CUF tells you energy delivered against the theoretical maximum if the plant ran at full power around the clock. Neither one requires guessing what the sun did.
This was a genuinely uncomfortable decision to make, because a dashboard with a PR field that's sometimes blank looks less complete than one that always has a number in it. We made it anyway, because a fabricated PR is worse than no PR. A plant owner making a decision off a guessed number doesn't know they're looking at a guess. A blank field at least tells the truth about what you don't know.
Where the same instinct shows up again
The same discipline shows up in how Suryamon scores string-level anomalies. Every string's current gets compared against the plant-wide value to detect underperformance, soiling, a failing connector, a dead string. The obvious way to do that comparison is against the mean. We use the median instead.
The reason is specific and worth stating plainly: a single dead string, current near zero, will drag the mean down with it. Compare every string against a mean that a dead string has already corrupted, and the dead string itself can end up looking only mildly underperforming relative to a lowered average, exactly the failure mode you built the check to catch, quietly defeating itself. The median doesn't move the same way. A handful of healthy strings around a median stays a stable reference point even when one string goes to zero, so the dead string still reads as dead instead of borderline.
It's a small implementation choice. It's also the same principle as the PR decision: don't let a metric look more trustworthy than the math underneath it actually is.
How we know the math itself is right
None of this matters if the KPI calculations are wrong, so every one of them is tested against IEC 61724-1 reference cases, the actual published standard, not our own assumptions about what the numbers should be. A 100 kWp plant receiving 5.5 kWh/m2 of irradiation and producing 440 kWh of output has to produce a PR of exactly 0.80, that's asserted in the test suite, not eyeballed.
The collector side gets the same treatment. The simulator's SunSpec Modbus server and the collector's reader talk over a real TCP connection inside the test suite, register encoding, model-chain walking, scale factors, all exercised on the actual wire protocol, not mocked. Every PromQL expression in every alert rule and every dashboard panel gets parsed by a real PromQL parser as part of CI, so a broken query fails the build instead of failing silently in front of a plant owner months later.
The dependency decision we made and documented
Suryamon pins pymodbus to the stable 3.6 line rather than tracking the latest release. The 3.7+ series is mid-migration to a new server API with breaking changes, and we didn't want Suryamon's reliability riding on a dependency that was still finding its own footing. This is written down directly in the codebase as a deliberate, documented choice, not something we're hoping nobody asks about. We'll move when the newer line actually stabilizes. Until then, the older, proven version is the correct choice, even though it's the less exciting one to ship.
The actual thesis
A monitoring tool's job is to tell you the truth about a plant, including the parts of the truth that are inconvenient, a metric it can't compute, a string that's failing, a dependency that isn't ready yet. The moment a tool starts filling gaps with plausible-looking numbers instead of admitting the gap, it has stopped being a monitoring tool and started being a guess with better formatting.
We'd rather ship the honest blank field than the confident wrong number. That's not a limitation of Suryamon. That's the whole point of it.
If this is a problem you're facing right now, get in touch.
Top comments (3)
I appreciate the decision to not provide a Performance Ratio number when irradiance data is missing, as it's better to have an honest blank field than a potentially misleading guessed value. I'm curious, have you considered providing an estimated uncertainty range for the specific yield and CUF metrics when irradiance data is available, to give users a sense of the potential variability in the measurements? This could help plant owners make more informed decisions, and it would be interesting to see how this approach could be implemented in Suryamon. I also think the use of median instead of mean for scoring string-level anomalies is a great choice, as it prevents a single dead string from skewing the results.
Really glad the median-over-mean detail landed, that's exactly the kind of decision that looks small until it fails in the field.
Honest answer on uncertainty ranges: not built yet, and it should be. Right now we report a point estimate, but that still implies more precision than the inverter's meter tolerance actually supports, typically ±1-2% for revenue-grade CTs. Doing it properly means propagating that tolerance through the calculation, not bolting on an arbitrary band. Same principle as the PR call: don't show more certainty than the data supports. Adding it to the roadmap.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.