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
One machine, one day, three shifts A 24-hour timeline from 06:00 to 06:00 divided into Shift A, B and C. Green bars show productive time, amber bars show idle time with the engine running, and gaps show the engine off. Per-shift totals are listed below each shift. Shift A 06:00–14:00 Shift B 14:00–22:00 Shift C 22:00–06:00 split at shift change midnight, still Shift C 06:00 10:00 14:00 18:00 22:00 02:00 06:00 Engine 07:10 Idle 01:50 Productive 05:20 Engine 06:10 Idle 01:50 Productive 04:20 Engine 05:40 Idle 01:20 Productive 04:20

Swipe sideways to see the full day.

This is the question the report answers, for every machine, every day. The timeline is illustrative. Activity that crosses a shift change is split and counted in both shifts.

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

  1. Download two exports from the tracking portal
  2. Match rows and assign shifts in Excel
  3. Calculate idle and productive hours by hand
  4. Split the sheet by vendor and yard
  5. Format and email each one, one by one

After

Automated, every morning

  1. Flows pull both APIs into SharePoint
  2. Power BI applies the shift rules
  3. Engine, idle and productive hours are calculated
  4. A paginated report renders a PDF per recipient
  5. 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.

  1. 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.

  2. 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.

  3. 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

  1. Around the clockThe machines work in three shifts. Each sensor records engine on and off times and the machine's movements.
  2. After the night shift endsTwo Power Automate flows sign in to the sensor platform and save the previous day's data to SharePoint.
  3. NextPower BI refreshes. It splits each machine's activity into shifts and calculates engine, idle and productive hours.
  4. ThenA third flow creates one PDF per vendor and yard and emails it. Each recipient sees only their own machines.
  5. 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.

Solution architecture JCBs and earth movers send sensor data to a telematics platform with an Engine running API and a Compound report API. Two scheduled Power Automate flows log in, fetch the daily data and save it to SharePoint folders named Daily Engine and Daily Compound, alongside a Vehicle master Excel file. A Power BI semantic model splits activity by shift with Power Query, links both sources through a bridge table, and calculates engine, idle and productive hours with DAX. It feeds an interactive report for the operations team and a paginated report built in Report Builder with vendor or yard and date parameters. A distribution flow exports one PDF per vendor or yard and emails it, so each inbox receives only its own machines. Source Ingestion Part 1 Storage Part 1 Power BI model Part 2 Delivery Part 3 Machine sensors Power Automate SharePoint Online Power BI service PDF and email Telematicsplatform API Engine running engine on/off time API Compound report idle time JCBs &earth moverssensor on each, 24/7 Engine flow Daily schedule Log in: user + session ID List report runs, filter Fetch rows, save file Compound flow Daily schedule Log in: user + session ID List report runs, filter Fetch rows, save file Daily Engine one file per day Daily Compound one file per day Vehicle master vendor or yard, reg no, PO no Full daily history,kept for audits Semantic model Power Query split activity by shift Bridge table machine × date × shift DAX measures engine, idle, productive Interactive report ops team explores any date, shift, vendor or machine Paginated report print-ready, same model vendor or yard + date Distribution flow Daily schedule Each vendor or yard Export PDF, filtered Email as attachment Vendor A Vendor B Yard 1 Each inbox gets onlyits own machines

Swipe sideways to explore the diagram.

End-to-end architecture. Power Automate ingests two telematics APIs into SharePoint, Power BI models shift-wise hours, and a paginated report is exported and emailed per vendor and yard.

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:

  1. Recurrence triggerThe flow runs automatically every day.
  2. Authenticate with an HTTP callThe flow calls the platform's login endpoint and captures the user ID and session ID it returns.
  3. Hold the session in variablesBoth IDs are stored in variables and passed to every request that follows.
  4. Get the report run listAn HTTP call lists the report runs available on the platform.
  5. Filter to the right runA Filter array step keeps only the runs for the required report and date.
  6. 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.
  7. 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.

How activity is split across shifts Top: an engine record from 13:20 to 15:05 crosses the 14:00 shift change and is cut into 40 minutes for Shift A and 65 minutes for Shift B. Bottom: a record from 23:10 on 18 February to 01:30 on 19 February crosses midnight but stays inside Shift C, so it is reported against 18 February. Crossing a shift change 13:00 14:00 15:00 14:00 shift change Raw 13:20 – 15:05, 1 h 45 min Split Shift A, 40 min Shift B, 65 min Crossing midnight 22:00 00:00 02:00 04:00 06:00 Shift C runs 22:00 – 06:00 midnight, the date changes Raw 23:10 – 01:30 18 Feb 19 Feb Reported as Shift C, 18 Feb

Swipe sideways to see the full timeline.

Both rules in action. Records are cut at shift boundaries, and night-shift activity after midnight is reported against the date the shift started.

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.

Data model Two fact tables, Engine Running Time and Compound Report, each relate many-to-one to a Bridge table on a composite key of machine, shifted date and shift. The Bridge table relates many-to-one to a Vehicle master that holds vendor or yard, registration or invoice number and PO number. Engine Running Time Key Machine Start, End Shifted Date, Shift duration sec Compound Report Key Machine Start, End Shifted Date, Shift duration sec Bridge Key (unique) Machine Date Shift Vehicle master Machine name Vendor or yard Reg or invoice no, PO no *1 *1 *1 Key = Machine & Shifted Date & Shift, built in Power Query on both fact tables Vehicle master is an Excel list kept in SharePoint

Swipe sideways to see the full model.

The bridge table gives both fact tables one shared grain: one row per machine, per date, per shift.

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.

RowsMachine ID, vendor, registration or invoice number, PO number and date
ColumnsShift A, B and C, plus a total for the day
ValuesEngine run, idle and productive hours (highlighted), with a remarks column
FiltersDate range, shift, vendor and machine ID, with a one-click Clear filters button

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.

  1. Publish the semantic modelThe Power BI model is published to the Power BI service, where it refreshes on a schedule from SharePoint.
  2. 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.
  3. 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.
  4. Publish to a Premium workspaceThe paginated report is published to a workspace on Premium capacity, which the export step requires.
  5. Distribute with Power AutomateA scheduled flow loops through the vendors and yards. For each one, the Export To File for Paginated Reports action renders a PDF using that recipient's parameter values, and an email action sends the file as an attachment.
One report design, many private PDFs A single paginated report is exported once per recipient by Power Automate, each time with different parameter values, producing a separate PDF for Vendor A, Vendor B and Yard 1 that is emailed to that recipient only. Paginated report one design, built once in Report Builder Export To File forPaginated Reportsruns once per recipient vendor = A PDF Vendor A, 18 Feb Vendor A vendor = B PDF Vendor B, 18 Feb Vendor B yard = 1 PDF Yard 1, 18 Feb Yard 1

Swipe sideways to see every recipient.

One report design, many private PDFs. The same paginated report is exported once per recipient, each time with different parameter values.
Vehicle compound & engine running report
Daily shift overview, durations in hh:mm
RecipientVendor ADateWed, 18 Feb 2026
Machine IDReg / Inv NoPO NoShift AShift BShift CRemarks
EngineIdleProductiveEngineIdleProductiveEngineIdleProductive
EX-01INV-0412PO-100107:5505:1802:3706:4002:1004:3005:2001:4503:35 
EX-02INV-0413PO-100108:0002:3905:2107:1203:0504:0704:1000:5503:15 
LD-03INV-0420PO-100206:2602:2304:0305:5003:0902:4100:0000:00  
Generated automatically from the Power BI semantic modelIllustrative data

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.

0manual steps in the daily reporting cycle
3shifts calculated for every machine, every day
2APIs ingested automatically each day
1private PDF per vendor or yard, per day
  • 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).

Client names and figures are withheld or illustrative to protect confidentiality.