Checking one by one eats the whole changeover
Twelve headsets, each one lifted to your face to confirm it loaded the right scene. That is ten minutes. You have three.
The control room for VR shows
DeviceHub Pro is a Meta Quest control room built for live performance. Batch connectivity, live monitoring, per-show OSC routing, and a pre-flight check that clears every unknown before the doors open. And when the work tours without you, the rights stay in your hands.
A headset is not a desktop. Guests take it off, apps drop to the home menu, Wi-Fi disappears, batteries die where you cannot see them. And you have three minutes between slots.
Twelve headsets, each one lifted to your face to confirm it loaded the right scene. That is ten minutes. You have three.
A guest hits Home, the app crashes, the battery hits zero. Until someone raises a hand, your desk shows nothing.
Two audiences, two cue sheets, one OSC port. The cue that advances show A advances show B with it.
A new APK lands and you sideload it one cable at a time — and every cable is another chance to break something.
Battery, running app, app version, IP, link state — one row per headset. Under 20% turns red on its own. Wrong app turns red on its own.
Groups A, B, C, D each hold their own room, ports, and headsets. Signals route by port, so two shows in the same room never hear each other.
Room verified, ports clear, headsets acknowledged — item by item on one page. All green is the only thing that reads "ready".
DeviceHub treats each running show as a show group. Every group owns an OSC input port and an output port — a cue arriving on one port moves that group's headsets and nothing else.
Click a port to fire a cue
Cue software
DeviceHub routing
Headsets
Waiting for a cue.
Cues crossed. This is what a single shared port looks like — the cue meant for A just advanced B as well.
| In | Out | |
|---|---|---|
| All | 9001 | 9000 |
| A | 9002 | 9200 |
| B | 9003 | 9201 |
| C | 9004 | 9202 |
| D | 9005 | 9203 |
Each headset listens on exactly one port — its own group's. Nothing changes on the headset. The all-call channel stays on 9001/9000 and reaches every headset at once.
The show status page lays out three things per show: is the room verified, are the ports clear, did the headsets acknowledge their assignment. The headsets report back themselves — the desk is not guessing.
Verifying room…
Checking ports…
A ⚠ is the headset that has not reported in — not a headset that might be off. You know exactly which one to walk to.
Connect over USB and DeviceHub switches the headset to Wi-Fi on its own. Then you pull the cable. From then on it just appears in the list.
Wi-Fi drops, the AP reboots, the whole floor vanishes at once. DeviceHub sees the total dropout and reconnects from its IP history. Nobody runs for a cable.
Identity is the serial number, not the address. New DHCP lease, new venue, new subnet — the name, the ID, and the group all follow it.
Identity resolves serial → UUID → IP history. Only when all three miss is a headset treated as new.
A headset belongs to exactly one show group at a time. The sub-group is its role in that show. The number is what you call out over comms.
Drag a headset into a sub-group — the number fills itself in
Unassigned
A · Sub-group 1
A · Sub-group 2
B · Sub-group 1
One running show. Holds the Photon room, the OSC ports, and every headset in it.
A role grouping inside that show, with its own name and colour.
The headset's number inside its sub-group, auto-assigned to the lowest free slot.
Numbers are unique within a sub-group. Move #3 out and the next headset in becomes #3.
Pick one APK and push it to every online headset. Success or failure per device with the reason, and a final count of both.
Bring the target app up or take it down across the floor in one click.
Polling runs on a 10-second cycle. After any launch or close it drops to a 3-second sweep for the next 10 seconds, so you see the result there and then instead of waiting for the next pass.
Installs run adb install -r (replace in place, data preserved), with a 120-second per-device timeout.
When a headset leaves the show app, DeviceHub can pull it back. The hard part is not the pulling — it is deciding whether it actually left. The Quest system menu and the Guardian boundary warning hold the foreground for a few seconds while the piece runs on behind them, and sometimes ADB cannot read the foreground at all. Treat any of that as a departure and you have yanked a guest out of the work for no reason.
Pick a scenario and watch how DeviceHub decides.
When ADB cannot read the foreground app, that is a failed measurement, not a headset leaving. DeviceHub treats it as no information: the count neither rises nor resets.
By default two polls in a row — about 20 seconds — have to agree before it counts as a departure. A system menu does not survive two rounds.
After a relaunch, each headset gets a 30-second cooldown, so a second launch never stacks on one that has not taken effect yet.
Even with auto-relaunch switched off, a departure still raises a notice in the corner. Knowing is useful even when you would rather walk over yourself. Coming back raises one too.
Auto-relaunch is off by default, and switched on per show group — on for this show, off for the one next door. Both thresholds are configurable in the config file: how twitchy a given venue's headsets are is not something the software can know.
Software that relaunches on its own is easy. Software that knows when to leave a headset alone is not.
The work is finished, and now it travels — to a curator, to an operating partner on another continent. But once the APK leaves your hands, nobody knows how many times it runs, and nobody knows whether it keeps running after the contract ends. XRPR is our project and licensing platform: every launch has to ask it for permission first, and the permission is yours to give.
XRPR is one acronym folded three ways. The operator does exactly one thing — enter a passphrase. Three layers run behind it, in order.
Valid until 2026-09-05
Not connected
Waiting to send.
Running
This show is unaffected — its licence was verified at launch
It starts with a passphrase. The Router does more than match a password: it reads that phrase as a set of coordinates and resolves the one path it corresponds to, among every project and room in the system.
In DeviceHub: the licence name and code you enter at launch, and the Photon room name and password each show group holds.
With the path resolved, the request reaches the relay — the security gate. It handshakes with the database in real time to confirm the phrase is inside its valid window, which project it belongs to, and whether it has been revoked. Only what passes goes through.
In DeviceHub: this layer is your switch. Revocations and date changes happen here and take effect immediately — their next launch will not get through.
The gate opens and the connection lands: a private room driven by Photon. Outside signals stay outside; the members inside interact in real time, at low latency.
In DeviceHub: each show group holds its own room. The password stays on the desk; headsets receive only the room name and region, which is everything Photon needs to join.
One passphrase from the operator, three layers behind it. And the middle one is always yours.
You open a licence in XRPR for this booking, set its dates to match the run, and hand it over together with DeviceHub. The operating team launches, enters it, and runs the show as normal. When the booking ends, the licence expires on its own. If the contract ends early, you revoke it in the dashboard — and their next launch simply will not open.
Two sides of one mechanism: you can withdraw the rights at any moment, and they will never be locked out in front of an audience.
| Link | ID | Name | Battery | IP | Serial | Running app | App version |
|---|---|---|---|---|---|---|---|
| 1 | V-01 | 84% | 192.168.10.101 | DEMO0000A001 | ShowApp | 1.4.2 | |
| 2 | V-02 | 77% | 192.168.10.102 | DEMO0000A002 | ShowApp | 1.4.2 | |
| 3 | V-03 | 18% | 192.168.10.103 | DEMO0000A003 | ShowApp | 1.4.2 | |
| 4 | V-04 | — | 192.168.10.104 | DEMO0000A004 | — | — |
Room SHOW-A · Jp · verified rev.3
OSC 9002 → 9200
8 · 8 · 8
Room SHOW-B · Jp · verified rev.1
OSC 9003 → 9201
[20:14:02] ===== Batch install started ===== [20:14:02] APK: show-build-1.4.2.apk [20:14:03] [1/8] V-01… [20:14:09] ✓ V-01 [20:14:16] ✓ V-02 [20:14:41] ✗ V-06 — INSTALL_FAILED_INSUFFICIENT_STORAGE [20:15:02] ===== Install complete ===== 7 / 1
[20:11:40] Discovery Server started, listening on: 9065 [20:11:41] Converted and found: 192.168.10.101:5555 [20:12:03] OSC Bypass[A]: /scene/change (2) → 4 [20:13:55] V-07 left ShowApp [20:14:15] Pulled back V-07
com.example.showapp109001 / 90009002 → 9200SHOW-A · Jp9003 → 9201SHOW-B · JpShow cover. The last thing you see before you go up is the name of tonight's show, not a wall of numbers.
Interface mock-ups below. All data shown is sample data.
Show cover. The last thing you see before you go up is the name of tonight's show, not a wall of numbers.Monitor cards on top, detail rows below: link, ID, name, battery, IP, serial, running app, app version, actions — all on one line.One card per show: room, ports, acknowledgements. All green is the only "ready".Pick an APK, start, and watch it land device by device.Live log with keyword filtering, rotating so it never fills the disk.Target app, ADB path, poll interval, OSC ports, show groups and sub-groups — all in one place.| Devices | Meta Quest family |
|---|---|
| Operating system | Windows 10 / 11 (primary); macOS build available |
| Connectivity | ADB over USB / Wi-Fi (TCP 5555) |
| Control protocol | OSC over UDP |
| All-call OSC ports | in 9001 / out 9000 |
| Per-group OSC ports | from 9002 in / 9200 out, one pair per group |
| Discovery | UDP 9065 (device broadcast) / 7908 and up (reply) |
| Default poll interval | 10 s (3 s for 10 s after any action) |
| Auto-relaunch | Off by default, enabled per show group; acts after 2 consecutive polls (~20 s), 30-second cooldown, thresholds configurable |
| Show groups | A–D by default, expandable |
| Measured scale | 50+ headsets per instance, measured in production |
| Licensing | XRPR licence, verified once per launch, with expiry dates and revocation on demand |
| Logging | 5 MB × 5 rotating files |
| Version | v0.2.1 |
Tell us your floor size and your run of show, and we will demo it against your actual setup. If you are planning to license a work to a curator or an operating partner abroad, we can walk through the licensing and handover flow as well.
The full document on the licensing model, OSC port layout, and Photon room delivery.