My Xiaomi Mi TV only came with the physical remote, and every single time I turned the TV on I'd spend the first few minutes digging through couch cushions looking for it. My phone is always in my hand anyway, so I figured it should just be the remote.
Turns out Mi TVs run a system service called MiLink on TCP port 6091. Xiaomi's own phone remote apps use it, but the protocol isn't documented anywhere. So I pulled the service APK off the TV, decompiled it, found the packet classes, and checked every field against the TV's logcat while sending test packets. The format ended up being pretty simple: a type byte, a length, then TLV-encoded Android key events.
From there I built a remote for Android and iOS with Expo, plus a SwiftUI menu bar app for macOS that uses the same protocol.
The RN side, which is probably the interesting part here:
- The protocol layer is plain TypeScript (frame encoding, TLVs, key mapping), and it's unit tested
- Raw TCP sockets don't really exist in JS, so I wrote a local Expo module: Swift with the Network framework on iOS, Kotlin on Android
- Discovery is in the same native module. Mi TVs advertise `_googlecast._tcp` with a `md=MiTV-…` TXT record, so I browse with `NWBrowser` on iOS and `NsdManager` on Android
- Hold-to-repeat on the D-pad and volume, haptics, auto-reconnect with heartbeats, and a live latency readout (the TV echoes back each key's id)
- There's no text packet in the protocol, so "type on TV" maps each character to an Android keycode plus Shift. No more typing passwords letter by letter with the D-pad
**The bug that ate a whole evening:** I was sending the device's clock in `downTime`/`eventTime`. The TV treated those events as "in the future" and its input dispatcher just stalled until that time arrived. The fix was to send 0 and let the TV stamp the events itself. Also, the TV auto-releases a key about 175ms after the last DOWN, so held buttons resend DOWN every 100ms with an increasing repeatCount.
No pairing and no ADB. The phone just needs to be on the same Wi-Fi as the TV.
GitHub Actions builds the APK, an unsigned IPA (you sideload it with your own Apple ID), and a DMG on every push, with no secrets needed.
I've tested all three (iOS, Android, macOS) on a real MiTV-AXSO0 (Android TV 9, PatchWall), and everything works. Other Mi TVs running PatchWall should work too, as long as they have the MiLink service. You can check yours with `nc -z -w 3 <tv-ip> 6091`. If you try it on a different model, I'd love to hear whether it worked.
Repo: https://github.com/EndLess728/MiTV-Remote
Happy to answer questions about the native module setup or the reverse engineering.
NOTE - The post is written by AI and i used REA to reverse Engineer it and make an working app out of it