Cross-Platform Mobile
Battalion App
The phone half of a Battalion toolbox, talking to the unit directly over Bluetooth Low Energy: pair to a box, read its battery, temperature and lock state, open it, and run the heater. Owners decide who gets to connect.
Visit live site
The context
A Rover toolbox opens two ways, from the keypad on the box or from a phone, and the phone half is this app. What joins the two is Bluetooth Low Energy, and that choice shapes everything above it: the phone speaks to the unit directly, in range, rather than asking a server to pass a message along. So the app answers what the keypad cannot, how much battery is left, what the temperature inside is, whether the box is locked right now, and it answers from the box itself. The harder part is that a box is rarely one person's. Crews share them, and the owner is often not the person standing in front of it, so connecting had to be something a person asks for and an owner grants, not something anyone within Bluetooth range can do.
What the work involved
- Built and shipped the app from a single React Native codebase to both iOS and Android.
- Implemented the Bluetooth Low Energy layer that carries every exchange with the hardware: discovering units in range, establishing and holding the connection, reading characteristics off the box, and writing commands back to it.
- Built onboarding as one continuous path: sign up, confirm through an emailed code with resend, then register the first toolbox by scanning the barcode or QR code on the unit itself.
- Built the device dashboard on top of those BLE reads, so battery level, interior temperature, and lock state are what the box reports now rather than a server's last known copy of it.
- Built the two controls the hardware exposes, a temperature adjustment for the onboard heater and a lock and unlock action, each written to the unit over BLE, with unlock kept behind the device code so that holding an unlocked phone is not the same as holding the box.
- Built pairing as a request rather than a self-serve action. A user picks a box and enters its code or device number, and the attempt resolves as approved or rejected: approval opens that device's dashboard, rejection returns the user to their own home screen.
- Split the profile surface by role. An owner sees the users connected to their devices and can disconnect any of them; a user sees only the devices they have been granted, plus their own account and password reset.
- Kept the two channels doing separate jobs: BLE is how the app reaches the hardware, while Firebase Auth and Firestore hold who is allowed to reach it. Being in range gets a phone as far as the box, not into it, and a connection revoked on the server is revoked here.
- Persisted account and device state on the phone, which is what keeps the app working past the edge of coverage. A job site is exactly where signal fails and BLE does not, so a crew member already granted a box can still reach it and open it with no connectivity at all, and the app catches up with the API once it has a connection again.
Outcome
One codebase on both platforms, and a box a crew can share without sharing a code. Access is requested, granted by the owner, and withdrawn from a phone instead of by changing the keypad. Because the link to the hardware is BLE and the state that authorises it is held locally, none of that stops working when the site has no signal.