This page explains how to use a small Python script together with Wireshark to capture IP-based control commands (TCP) that are sent by controllers such as MyURemote, Roomie or any other app.
The idea is simple: instead of sending commands directly to a device (for example a Global Caché or an Onkyo receiver), you temporarily send them to a capture PC. On that PC a fake device server is running that logs everything and optionally forwards it. Wireshark can then be used to inspect the packets in detail.
This is very useful if:
sendir,...).For Global Caché IR commands we typically use TCP port 4998. For fake receivers (e.g. Onkyo IP control) you can choose the port according to the original device protocol.
Save the following script as fake_device_server.py on your capture PC.
It listens on a TCP port (and UDP on the same port), logs all incoming data in
both HEX and ASCII, and sends a minimal OK reply for TCP.
#!/usr/bin/env python3
import socket
import socketserver
import datetime
import sys
import threading
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
except Exception:
ip = "127.0.0.1"
finally:
s.close()
return ip
def log_packet(data, client_ip, client_port, proto):
ts = datetime.datetime.now().isoformat(timespec="seconds")
print(f"[{ts}] {proto} {client_ip}:{client_port} -> {len(data)} bytes")
hex_str = " ".join(f"{b:02X}" for b in data)
print(f" HEX: {hex_str}")
ascii_str = "".join(chr(b) if 32 <= b < 127 else "." for b in data)
print(f" ASCII: {ascii_str}")
sys.stdout.flush()
class TCPHandler(socketserver.BaseRequestHandler):
def handle(self):
client_ip, client_port = self.client_address
print(f"\n=== New TCP connection from {client_ip}:{client_port} ===")
while True:
data = self.request.recv(4096)
if not data:
print(f"--- TCP connection closed ({client_ip}:{client_port}) ---")
break
log_packet(data, client_ip, client_port, "TCP")
try:
self.request.sendall(b"OK\r\n")
except Exception:
pass
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
daemon_threads = True
class UDPHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request[0]
client_ip, client_port = self.client_address
log_packet(data, client_ip, client_port, "UDP")
class UDPServer(socketserver.UDPServer):
allow_reuse_address = True
def main():
print("=== Fake Device Server (TCP + UDP) ===")
port = int(input("Port to listen on (e.g. 4998): "))
local_ip = get_local_ip()
print(f"\nLocal server IP: {local_ip}")
print(f"Set this IP and port in your controller (MyURemote, Roomie, ...).")
print(f"Wireshark filter example: host {local_ip} and tcp.port == {port}")
print(f"Or for UDP: host {local_ip} and udp.port == {port}\n")
host = "0.0.0.0"
tcp_server = ThreadedTCPServer((host, port), TCPHandler)
tcp_thread = threading.Thread(target=tcp_server.serve_forever, daemon=True)
tcp_thread.start()
print(f"TCP server listening on {host}:{port}")
udp_server = UDPServer((host, port), UDPHandler)
udp_thread = threading.Thread(target=udp_server.serve_forever, daemon=True)
udp_thread.start()
print(f"UDP server listening on {host}:{port}")
print("\nWaiting for traffic... (Ctrl+C to stop)\n")
try:
while True:
threading.Event().wait(3600)
except KeyboardInterrupt:
print("\nStopping fake server...")
tcp_server.shutdown()
udp_server.shutdown()
tcp_server.server_close()
udp_server.server_close()
print("Stopped.")
if __name__ == "__main__":
main()
fake_device_server.py on your capture PC.python fake_device_server.py (Windows) or python3 fake_device_server.py (macOS/Linux).192.168.0.179).On the first run, your OS firewall may ask to allow incoming connections for Python. Make sure to allow this, otherwise your iPad/iPhone will not be able to connect.
On your controller (for example an iPad running MyURemote or Roomie):
192.168.0.179).Now, when you press a button in MyURemote or Roomie, the command will be sent to the fake device server on your capture PC instead of the real device.
host 192.168.0.179 and tcp.port == 4998You now have two views of the same data:
fake_device_server.py shows HEX and ASCII.A typical captured Global Caché IR command looks like this in ASCII:
sendir,2:1,1,37000,1,1,12,11,12,22,12,11,24,22,12,11,24,22,24,22,12,11,24,22,24,22,24,22,12,740.
This contains module and connector (2:1), the carrier frequency
(37000 Hz) and all burst-pairs that define the IR waveform. You can
store this as-is in your own database or convert it to another format.
For an IP-controlled receiver (for example Onkyo), you can configure MyURemote or Roomie to send commands to the capture PC instead of the real receiver. Even if the receiver is not present, the app will try to send its IP commands and the fake server will log them. This is very useful to reverse-engineer eISCP strings or other proprietary protocols.
OK\r\n for TCP connections to avoid timeouts.