Case study
Automating shift-wise utilization reports for JCBs and earth movers with Power BI and Power Automate
At a large steel plant in India, every JCB and earth mover carries a sensor that starts recording the moment the machine is switched on. We turned that sensor data into a daily report of engine, idle and productive hours for every machine and every shift. Each vendor and yard now gets a print-ready PDF of only its own machines, every day, without anyone building or sending it.
- Industry
- Steel manufacturing, yard operations
- Machines
- JCBs and other earth movers, each with an on-board sensor
- Built with
- Power BI, Report Builder, Power Automate, SharePoint
- Delivery
- Daily PDF by email, fully automated
One machine, one day, three shifts
- Productive
- Idle, engine running
- Engine off
Swipe sideways to see the full day.
Project at a glance
The challenge
Every day, someone downloaded two sensor reports, matched them up by hand, worked out each shift's hours and emailed every vendor separately. It was slow and easy to get wrong. One wrong attachment could even send a vendor someone else's data.
What we built
Power Automate collects the sensor data every day. Power BI turns it into engine, idle and working hours for each shift. Then every vendor and yard gets a PDF of only its own machines, by email, automatically.
The result
A daily report that builds and sends itself. The same rules every day, no data sent to the wrong person, and a full history for audits and billing checks.
The challenge: a daily report nobody should have to build by hand
Large steel plants run on heavy earth-moving equipment. JCBs, excavators, loaders and other earth movers move raw material, clear the yards and keep production going around the clock, across three shifts. The machines are hired from several vendors and work across different yards.
Every machine has a sensor, a small telematics device, fitted to it. The moment the machine is switched on, the sensor starts recording. It logs when the engine is on and off, and it also records how the machine moves, such as its arm movements. That is how working time can be told apart from waiting time, and it matters, because an engine that's running isn't necessarily working. A machine that idles for hours burns fuel and adds engine hours without moving any material.
The data existed. Turning it into a usable report did not happen on its own. Every day, someone on the operations team had to:
- download two separate reports from the tracking portal, one for engine running time and one for idle time
- line the rows up machine by machine and work out which hours fell in which shift, including night shifts that run past midnight
- subtract idle time from engine time to get productive hours
- split everything by vendor and yard, format it, and email each recipient separately
Hours lost, every day
A repetitive compile-and-send routine that ate into skilled staff time daily.
Room for error
One wrong formula or a miscounted shift boundary changes a vendor's hours, and with them, the bill.
Confidentiality risk
Attach the wrong file and one vendor sees another vendor's machines and figures.
No usable history
Numbers lived in one-off downloads, so answering "what happened last month?" meant digging through old files.
The client was also clear about the output. Vendors needed a properly formatted PDF they could file, print and forward, not a table pasted into the body of an email.
Before
Manual, every morning
- Download two exports from the tracking portal
- Match rows and assign shifts in Excel
- Calculate idle and productive hours by hand
- Split the sheet by vendor and yard
- Format and email each one, one by one
After
Automated, every morning
- Flows pull both APIs into SharePoint
- Power BI applies the shift rules
- Engine, idle and productive hours are calculated
- A paginated report renders a PDF per recipient
- Each vendor and yard gets its own PDF by email
The solution in plain English
Think of a utility bill. Meter readings arrive automatically, the calculations happen in one place, and every customer receives a statement showing only their own usage. We built the same thing for machine hours.
-
Collect
Every day, two automated flows sign in to the telematics platform, pull the latest data from its two APIs and save it in SharePoint. Over time this builds a complete, day-by-day history.
-
Calculate
Power BI reads those files, splits each machine's activity into Shift A, B and C, and works out engine run, idle and productive hours. The operations team gets an interactive report to explore any date, shift, vendor or machine.
-
Deliver
A print-ready paginated report turns the same numbers into a polished PDF. A second flow creates one PDF per vendor or yard and emails it, so every recipient sees only their own machines.
Productive hours = Engine run hours − Idle hours The one formula behind the whole report, applied the same way to every machine, shift and day.
The Microsoft tools, explained
You don't need to know these tools to benefit from them. Here is what each one is, the job it does in this project, and why we chose it.
Power Automate
Microsoft's tool for automating routine tasks. It works like a digital assistant that follows a fixed set of steps on a schedule.
Its job here
- Signs in to the sensor platform every day and downloads the latest engine and idle records
- Saves each day's records as a file in SharePoint
- Creates one PDF per vendor and yard, and emails each one to the right person
Why we chose it: it runs in the cloud with no server to maintain, connects to the sensor platform's API, and keeps a log of every run.
SharePoint Online
Secure file storage in Microsoft 365, the same place many teams keep their documents.
Its job here
- Keeps every day's sensor data, building a full history
- Holds the vehicle master list: each machine's vendor, yard, registration and PO number
Why we chose it: the client already uses Microsoft 365, and stored files make refreshes reliable and keep a record for audits.
Power BI
Microsoft's tool for turning raw data into calculations and interactive reports.
Its job here
- Cleans the sensor records and splits every machine's activity into Shift A, B and C
- Calculates engine, idle and productive hours with one set of rules
- Gives the operations team an interactive report for any date, shift, vendor or machine
Why we chose it: the calculations live in one place, so the dashboard and every PDF always show the same numbers.
Power BI Report Builder
The Power BI tool for print-ready "paginated" reports, like invoices or statements.
Its job here
- Designs the PDF each vendor receives: a fixed layout with headers and clean page breaks
- Filters the report to one vendor or yard and one day at a time
Why we chose it: vendors needed a document they can file, print and forward, not a table pasted into an email.
A day in the life of the system
- Around the clockThe machines work in three shifts. Each sensor records engine on and off times and the machine's movements.
- After the night shift endsTwo Power Automate flows sign in to the sensor platform and save the previous day's data to SharePoint.
- NextPower BI refreshes. It splits each machine's activity into shifts and calculates engine, idle and productive hours.
- ThenA third flow creates one PDF per vendor and yard and emails it. Each recipient sees only their own machines.
- During the dayThe operations team checks any machine, shift or vendor in the interactive report. Nobody builds a spreadsheet.
Solution architecture
Here's how the pieces connect end to end. Data moves left to right, from the sensors on each machine to a PDF in each recipient's inbox. The three parts below follow the same order.
Swipe sideways to explore the diagram.
Technical deep dive
For data and IT teams, here's how each layer works and why we designed it this way. The build has three parts, in the order the data flows.
Part 1: Collecting the sensor data with Power Automate and SharePoint
Power Automate (HTTP actions) and SharePoint Online
In plain words: Power Automate is the assistant that fetches the data. Every day it signs in to the sensor platform, just as a person would, downloads the latest engine and idle records, and files them in SharePoint. Nobody has to remember to do it.
The telematics platform exposes two APIs, and each one feeds a different measure:
- Engine running API: the intervals when each machine's engine was on. This gives engine run hours.
- Compound report API: the intervals the platform classifies as idle, using the sensor's movement data such as arm movement. This gives idle hours.
We built one scheduled flow per API. Both follow the same pattern:
- Recurrence triggerThe flow runs automatically every day.
- Authenticate with an HTTP callThe flow calls the platform's login endpoint and captures the user ID and session ID it returns.
- Hold the session in variablesBoth IDs are stored in variables and passed to every request that follows.
- Get the report run listAn HTTP call lists the report runs available on the platform.
- Filter to the right runA
Filter arraystep keeps only the runs for the required report and date. - Get the report dataFor each run, another HTTP call fetches the row-level records, and a nested loop appends every row to an array variable.
- Write to SharePointThe rows are staged through Excel Online and saved as that day's file in the matching SharePoint folder, Daily Engine or Daily Compound.
Why SharePoint sits between the API and Power BI
Connecting Power BI straight to the API looks simpler. In practice it's fragile. Adding SharePoint as an intermediate layer gave us five things:
- Reliable refreshesAPIs have call limits and time-outs, so a refresh that calls them directly fails at random. Reading files from SharePoint is fast and predictable.
- A complete historyAPIs usually serve recent data only. SharePoint keeps every day, so trends, audits and disputes can go all the way back to go-live.
- Simple recoveryIf one day's pull fails, re-run that flow and the report corrects itself at the next refresh. Nothing downstream changes.
- Credentials in one placeAPI logins live only in Power Automate. Power BI connects to SharePoint Online with organizational sign-in, so no secrets sit in the dataset and no gateway is needed.
- An auditable raw layerThe daily files are an exact record of what the platform reported, kept separate from any business logic.
Part 2: The Power BI model behind the report
Power Query, data modeling and DAX
In plain words: Power BI is the calculator and the dashboard. It takes the raw sensor records, works out which shift every minute belongs to, and calculates engine, idle and productive hours that everyone can trust.
This is the heart of the solution. The raw data is a list of time intervals per machine. The business wants hours per machine, per day, per shift. Getting from one to the other meant solving three problems.
Problem 1: activity that crosses a shift change
The plant runs Shift A from 06:00 to 14:00, Shift B from 14:00 to 22:00 and Shift C from 22:00 to 06:00. A machine that starts work at 13:20 and stops at 15:05 worked in two shifts, and each shift should get its share.
In Power Query, we generate split points at 06:00, 14:00 and 22:00 for every day a record spans, cut the record into segments at those points, and share the reported duration across the segments in proportion to their length.
Problem 2: a night shift that belongs to yesterday
Shift C starts at 22:00 and ends at 06:00 the next morning. Activity at 01:30 on 19 February belongs to the night shift of 18 February. A calculated Shifted Date moves any segment that starts before 06:00 back one day, so each night shift is reported as a single unit on the day it began.
Swipe sideways to see the full timeline.
Power Query (M), simplified
// 1. Cut each record at 06:00, 14:00 and 22:00
shiftTimes = {#time(6,0,0), #time(14,0,0), #time(22,0,0)},
validBoundaries = List.Select(boundaries, each _ > start and _ < end),
splitPoints = List.Sort(List.Combine({{start}, validBoundaries, {end}})),
// 2. Share the reported duration across the segments
NewSec = [OriginalSec] * ([SegmentSec] / [TotalSec]),
// 3. Night shift belongs to the day it started
#"Shifted Date" = if Time.Hour([SegStart]) < 6
then Date.AddDays(Date.From([SegStart]), -1)
else Date.From([SegStart]),
Shift = let h = Time.Hour([SegStart]) in
if h >= 6 and h < 14 then "Shift A"
else if h >= 14 and h < 22 then "Shift B"
else "Shift C"
Problem 3: two data sources, one row per shift
Engine time and idle time arrive in separate tables, and a given shift can appear in one but not the other. We gave both tables the same composite key, Machine & Shifted Date & Shift, and built a bridge table from every distinct key in both sources. The bridge is split back into Machine, Date and Shift columns and linked to a vehicle master, an Excel list in SharePoint that supplies the vendor or yard, registration or invoice number and PO number.
Swipe sideways to see the full model.
The result: every machine-shift combination appears exactly once, and engine and idle figures always line up in the same row, even on days when one source has no records.
Measures that stay correct past 24 hours
Durations are summed in seconds and formatted as hh:mm text. Power BI's time type can't show more than 23:59, so a machine that logged 30 hours 15 minutes across a date range would display wrongly. Formatting the total ourselves keeps it right. Productive hours are engine seconds minus idle seconds:
DAX
Productive Hrs =
VAR EngineSec = SUM ( 'Engine Running Time'[duration sec] )
VAR IdleSec = SUM ( 'Compound Report'[duration sec] )
VAR s = EngineSec - IdleSec
VAR H = INT ( s / 3600 )
VAR M = INT ( MOD ( s, 3600 ) / 60 )
RETURN
IF ( H = 0 && M = 0, BLANK (), FORMAT ( H, "00" ) & ":" & FORMAT ( M, "00" ) )
One table, on purpose
The report has a single main visual: a matrix. We considered charts and deliberately left them out. The people reading this report check hours against hire bills and shift logs. They need exact figures for every machine, day and shift, not trend lines.
Part 3: Pixel-perfect PDFs with Report Builder and automated email delivery
Power BI service, Power BI Report Builder and Power Automate
In plain words: Report Builder designs the printed page, and Power Automate is the postman. Every day it prints one PDF per vendor or yard and emails it to the right person.
Power Automate on its own can email Power BI data as an HTML table in the body of a message. That wasn't good enough here. Vendors needed a proper PDF that looks the same on every screen and printer, with a clean header, a fixed layout and tidy page breaks. That's exactly what paginated reports are designed for, so we added one to the workflow.
- Publish the semantic modelThe Power BI model is published to the Power BI service, where it refreshes on a schedule from SharePoint.
- Build the paginated report on the same modelIn Power BI Report Builder we connected to the published semantic model and rebuilt the table for print. The dashboard and the PDF use the same DAX measures, so there's one source of truth and no duplicated logic.
- Add report parametersWe defined parameters for vendor or yard and for date, wired into the dataset query. They're what let an outside process ask for one recipient's report for one day.
- Publish to a Premium workspaceThe paginated report is published to a workspace on Premium capacity, which the export step requires.
- Distribute with Power AutomateA scheduled flow loops through the vendors and yards. For each one, the
Export To File for Paginated Reportsaction renders a PDF using that recipient's parameter values, and an email action sends the file as an attachment.
Swipe sideways to see every recipient.
| Machine ID | Reg / Inv No | PO No | Shift A | Shift B | Shift C | Remarks | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Engine | Idle | Productive | Engine | Idle | Productive | Engine | Idle | Productive | ||||
| EX-01 | INV-0412 | PO-1001 | 07:55 | 05:18 | 02:37 | 06:40 | 02:10 | 04:30 | 05:20 | 01:45 | 03:35 | |
| EX-02 | INV-0413 | PO-1001 | 08:00 | 02:39 | 05:21 | 07:12 | 03:05 | 04:07 | 04:10 | 00:55 | 03:15 | |
| LD-03 | INV-0420 | PO-1002 | 06:26 | 02:23 | 04:03 | 05:50 | 03:09 | 02:41 | 00:00 | 00:00 | ||
Swipe sideways to see the full page.
The results
The reporting routine that used to start every morning now runs on its own, and the numbers it produces are the same no matter who looks at them.
- Time back for the operations teamThe daily compile-and-send routine is gone. People now review exceptions instead of building spreadsheets.
- Numbers everyone can trustThe same shift and hour rules run every day, so there's no formula drift between people or between days.
- The right data for the right personEach vendor and yard receives only its own machines. Confidentiality is part of the design, not left to whoever attaches the file.
- Faster billing checksRegistration, invoice and PO numbers on every line make it easy to check hours against hire bills.
- A history to learn fromEvery day's data is kept, so utilization trends, idle-time patterns and disputes can be reviewed months later.
- Easy to extendNew machines are added to the vehicle master and flow straight into the report and the PDFs, with no rework.
Where else this approach works
Nothing here is specific to steel or to JCBs. If your machines or vehicles have sensors, and you send partners a regular report, the same pattern works for you.
- Construction and equipment rentalMachine hours and idle time per contractor or site, ready for hire billing.
- Mining and quarryingHaul truck and loader utilization by shift, pit and operator.
- Logistics and transportVehicle utilization and trip summaries for each transporter.
- ManufacturingMachine runtime and downtime by line, shift or contractor.
- Facility managementAsset runtime and service reports for each client site.
- Distribution and retailDealer- or branch-specific statements, delivered every morning.
Frequently asked questions
How does the system know whether a machine is working or idle?
A sensor on each JCB or earth mover starts recording when the machine is switched on. It logs engine on and off times and the machine's movements, such as arm movements. The telematics platform uses these signals to separate working time from idle time, and Power BI then calculates productive hours as engine hours minus idle hours.
Can Power BI send each recipient a report with only their data?
Yes. A paginated report with parameters, exported by Power Automate in a loop, produces one PDF per recipient filtered to their records. For interactive access inside Power BI, row-level security does the same job.
Do we need Power BI Premium to email PDF reports automatically?
For the Export To File for Paginated Reports action, yes. The report must sit in a workspace on Premium or Fabric capacity (P1 or F2 and above, or A4 to A6). Premium Per User also works, but it is limited to one export every five minutes.
Why not connect Power BI directly to the API?
API call limits and time-outs make direct refreshes unreliable, and most APIs only serve recent data. Landing daily files in SharePoint gives dependable refreshes, a full history and easy recovery when a day fails.
Can Power Automate send a Power BI report without a paginated report?
It can send data as an HTML table in the email body, which is fine for quick alerts. For a branded, print-ready PDF attachment, a paginated report is the right tool.
Will this work with our GPS or telematics provider?
If your platform offers an API or a scheduled export, almost certainly. Only the ingestion flow is specific to the provider. The model and delivery layers stay the same.
Still building reports by hand?
KS AI & Cloud Solutions designs and builds Power BI, Power Automate and Microsoft Fabric solutions that take repetitive reporting off your team's plate. Tell us what you send today, and we'll show you what it looks like automated.
Led by a Microsoft Certified Power BI Data Analyst (PL-300).