Knowing how to calculate OEE correctly matters more than the number itself, because small definition choices can move the result by many points. This guide explains the three factors, works through a complete shift example, lists the mistakes that quietly inflate OEE, and shows Python that calculates it from machine-state messages published over MQTT. By the end you can compute OEE by hand and from live data, and defend every input.
What OEE Measures
Overall Equipment Effectiveness compares what a machine actually produced with what it could have produced in the time it was scheduled to run. It answers one question: of the planned production time, how much was spent making good parts at the ideal speed?
OEE combines three losses:
- Availability captures stops: breakdowns, changeovers and waiting.
- Performance captures speed: running slower than ideal and short stops that are not logged as downtime.
- Quality captures defects: scrap and rework.
Multiplying the three factors gives OEE. A machine with perfect availability and quality but running at half speed has 50 percent OEE, which is exactly the signal you want.
The OEE Formula
| Factor | Formula | Uses |
|---|---|---|
| Availability | Run time / Planned production time | Stop durations |
| Performance | (Ideal cycle time x Total count) / Run time | Count and ideal cycle time |
| Quality | Good count / Total count | Good and reject counts |
| OEE | Availability x Performance x Quality | All three |
An equivalent shortcut is OEE = (Good count x Ideal cycle time) / Planned production time. It produces the same result and is a useful check on your arithmetic. The three-factor version is still worth calculating, because it tells you where the losses are.
Definitions that must be fixed before you start:
- Planned production time is shift time minus planned stops such as breaks and planned maintenance. Whether changeovers count as planned or unplanned is a site decision; most lean practitioners treat them as availability losses so they stay visible.
- Run time is planned production time minus all unplanned stop time.
- Ideal cycle time is the fastest the machine can reliably make the part, usually from the equipment design or a demonstrated best rate, not the standard used for costing.
- Total count includes good and rejected parts.
Worked Example: One Shift on a Filling Machine
Take an eight-hour shift (480 minutes) with two 15-minute breaks.
- Planned production time = 480 - 30 = 450 minutes.
- Stops during the shift: a 25-minute changeover and a 20-minute breakdown, so 45 minutes of downtime.
- Run time = 450 - 45 = 405 minutes.
- Ideal cycle time is 1.0 second per unit, so the ideal rate is 60 units per minute.
- Total count = 21,000 units.
- Rejects = 420 units, so good count = 20,580.
Now the factors:
- Availability = 405 / 450 = 0.900 (90.0 percent)
- Performance = (1.0 s x 21,000) / (405 x 60 s) = 21,000 / 24,300 = 0.864 (86.4 percent)
- Quality = 20,580 / 21,000 = 0.980 (98.0 percent)
- OEE = 0.900 x 0.864 x 0.980 = 0.762 (76.2 percent)
Check with the shortcut: (20,580 x 1.0 s) / (450 x 60 s) = 20,580 / 27,000 = 0.762. The two methods agree.
What does this tell the team? The biggest single loss is performance: in 405 minutes of running, the machine made 3,300 fewer units than it could have. That loss is made of micro-stops and reduced speed, which rarely appear in a manual log. The packaging line monitoring article shows how to break performance loss down further.
Common Mistakes When Calculating OEE
These are the errors that make OEE look better than reality or make machines impossible to compare:
- Using a slow ideal cycle time. If the "ideal" is the planning standard with padding, performance can exceed 100 percent or look artificially high. Use the true design or best demonstrated rate.
- Excluding changeovers. Calling every changeover "planned" removes a major improvement opportunity from view.
- Counting good parts only in performance. Performance uses total count. Using good count double-counts quality loss.
- Averaging OEE percentages across machines. A small machine and a large one should not have equal weight. Sum the underlying times and counts, then calculate.
- Counting at every station on a line. For a line, OEE should use the output of the last station and the ideal rate of the constraint.
- Mixing products with different cycle times. Calculate ideal time per product (count x ideal cycle time for each product) and sum it, rather than using one rate for the whole shift.
For multi-product shifts, the shortcut form handles mixes cleanly: OEE = sum over products of (good count x ideal cycle time) / planned production time.
Choosing the Ideal Cycle Time
The ideal cycle time is the input most often disputed, and it has the largest effect on performance. Three sources are common:
- Nameplate or design rate from the equipment supplier. Honest, but sometimes unreachable for a given product.
- Best demonstrated rate, the fastest cycle the machine has sustained for a meaningful period on that product. Practical and defensible.
- Standard rate from costing or planning. Usually padded and therefore the wrong choice for OEE.
Pick the design rate or the best demonstrated rate, record it per product and machine, and change it only through a deliberate review. When you keep master data like this in a retained MQTT message per machine (for example a config topic on your ConnectMQ broker), every calculation service and dashboard reads the same value, and a change is visible to everyone at once.
How to Calculate OEE from MQTT Machine Data
Hand calculation is fine for a training session, but in practice OEE should come from machine signals. A typical setup publishes two kinds of messages per machine to an MQTT broker:
connectmq/demo/<your-id>/plant1/line2/filler/state {"state": "RUNNING", "ts": 1792130400}
connectmq/demo/<your-id>/plant1/line2/filler/counts {"total": 21000, "reject": 420, "ts": 1792159200}
State messages are published on every change; counts are cumulative for the shift and published periodically. The function below calculates OEE for a window from a list of state events and the start and end counts. It treats PLANNED_STOP as excluded from planned time and every other non-running state as downtime.
from dataclasses import dataclass
@dataclass
class StateEvent:
ts: float # unix seconds
state: str # RUNNING, DOWN, IDLE, SETUP, PLANNED_STOP
def calculate_oee(events, window_start, window_end, total_count, reject_count, ideal_cycle_s):
"""Return availability, performance, quality and OEE for one window."""
events = sorted(events, key=lambda e: e.ts)
planned_stop = 0.0
run_time = 0.0
for i, ev in enumerate(events):
start = max(ev.ts, window_start)
end = events[i + 1].ts if i + 1 < len(events) else window_end
end = min(end, window_end)
if end <= start:
continue
duration = end - start
if ev.state == "PLANNED_STOP":
planned_stop += duration
elif ev.state == "RUNNING":
run_time += duration
planned_time = (window_end - window_start) - planned_stop
if planned_time <= 0 or run_time <= 0 or total_count <= 0:
return {"availability": 0.0, "performance": 0.0, "quality": 0.0, "oee": 0.0}
availability = run_time / planned_time
performance = min((ideal_cycle_s * total_count) / run_time, 1.0)
quality = (total_count - reject_count) / total_count
return {
"availability": round(availability, 4),
"performance": round(performance, 4),
"quality": round(quality, 4),
"oee": round(availability * performance * quality, 4),
}
if __name__ == "__main__":
t0 = 0
shift = [
StateEvent(t0, "RUNNING"),
StateEvent(t0 + 120 * 60, "PLANNED_STOP"), # break
StateEvent(t0 + 135 * 60, "SETUP"), # changeover 25 min
StateEvent(t0 + 160 * 60, "RUNNING"),
StateEvent(t0 + 300 * 60, "PLANNED_STOP"), # break
StateEvent(t0 + 315 * 60, "RUNNING"),
StateEvent(t0 + 400 * 60, "DOWN"), # breakdown 20 min
StateEvent(t0 + 420 * 60, "RUNNING"),
]
print(calculate_oee(shift, t0, t0 + 480 * 60, 21000, 420, 1.0))
Running it reproduces the worked example: availability 0.9, performance 0.8642, quality 0.98, OEE 0.7622. Note the min(..., 1.0) cap on performance. If you hit it regularly, the ideal cycle time is wrong; fix the master data instead of relying on the cap.
To turn this into a live number, subscribe to the state and count topics, keep the events for the current shift in memory, and recalculate every minute. The real-time OEE monitoring tutorial builds exactly that service, and the OEE vs TEEP comparison explains what changes when you measure against calendar time instead of planned time.
Trying It on a Real Broker
You can test the message flow without any hardware using the free public MQTT broker at broker.connectmq.com. Publish a state change by hand:
mosquitto_pub -h broker.connectmq.com -p 1883 -i oee-test-h4v7 -t "connectmq/demo/h4v7/plant1/line2/filler/state" -m '{"state":"RUNNING","ts":1792130400}' -r
Remember that the public area is readable by anyone. For real production data, ConnectMQ gives you a private hosted MQTT broker with credentials, TLS and a portal where you can watch the topics live. For how OEE fits with other plant metrics, see the smart manufacturing guide.
Get Started with ConnectMQ
Create a free Serverless broker at app.connectmq.com with no credit card, publish your machine states and counts, and calculate OEE from real data instead of end-of-shift estimates.
Frequently Asked Questions
What is a good OEE score?
It depends on the process and how strictly you define the inputs. Use OEE to track improvement on the same machine with fixed definitions rather than to compare against generic benchmarks.
Can performance be higher than 100 percent?
Not if the ideal cycle time is correct. A performance above 100 percent means the ideal cycle time is too slow and should be corrected.
Should changeovers count against availability?
Most improvement programs include them as availability loss so setup reduction stays visible. Whatever you choose, apply it consistently across machines and shifts.
How do I calculate OEE for a whole line?
Use the output count of the final station, the ideal rate of the constraint machine and line-level stop time. Do not average the OEE of individual machines.



