diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a4dae1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +__pycache__ diff --git a/README.md b/README.md index 8a4a0e6..bedf7c9 100644 --- a/README.md +++ b/README.md @@ -31,61 +31,58 @@ **iRemote** is a web-based universal IR blaster interface powered by a Python backend. Plug in a USB IR blaster, run the server, and control any infrared device from any browser — no WebUSB or special browser extensions required. Works with cheap USB IR blasters you can find on AliExpress for $3–8: + - **Tiqiaa** (uses the ZaZa Remote app on mobile) - **Ocrustar / ElkSmart** (uses the Smart IR Blaster app on mobile) ## Features -| Feature | Description | -|---------|-------------| -| 🎮 **Universal Remote** | TV remote with multi-blast (Samsung + LG + Sony + Philips simultaneously) | -| 🏨 **Hotel TV Toolkit** | Pre-built macros for hotel/hospitality TV menus — Samsung, LG, Sony, Philips | +| Feature | Description | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| 🎮 **Universal Remote** | TV remote with multi-blast (Samsung + LG + Sony + Philips simultaneously) | +| 🏨 **Hotel TV Toolkit** | Pre-built macros for hotel/hospitality TV menus — Samsung, LG, Sony, Philips | | 📚 **IR Database Browser** | Browse 300,000+ IR codes from [Flipper-IRDB](https://github.com/Lucaslhm/Flipper-IRDB) and [irdb](https://github.com/probonopd/irdb) | -| 📂 **File Import** | Flipper Zero `.ir`, IRDB `.csv`, Pronto hex, raw pulse data, iRemote Android `.json` | -| 🔴 **IR Learning** | Capture signals from physical remotes (point and press) | -| ⚡ **Batch Learn** | Pre-define button names, learn them one-by-one — fast full remote capture | -| ❄️ **AC / Fan Remotes** | Temperature control, mode switching, fan speed | -| 📡 **Protocol Sender** | Manual NEC / Samsung32 / RC5 / Sony SIRC with hex address + command | -| 💾 **Saved Remotes** | Persist custom remotes, export/import as JSON | -| 🔧 **Custom Macros** | Build your own IR button sequences with delays | -| 📋 **Full Logging** | See every USB frame, protocol encode, and device response | +| 📂 **File Import** | Flipper Zero `.ir`, IRDB `.csv`, Pronto hex, raw pulse data, iRemote Android `.json` | +| 🔴 **IR Learning** | Capture signals from physical remotes (point and press) | +| ⚡ **Batch Learn** | Pre-define button names, learn them one-by-one — fast full remote capture | +| ❄️ **AC / Fan Remotes** | Temperature control, mode switching, fan speed | +| 📡 **Protocol Sender** | Manual NEC / Samsung32 / RC5 / Sony SIRC with hex address + command | +| 💾 **Saved Remotes** | Persist custom remotes, export/import as JSON | +| 🔧 **Custom Macros** | Build your own IR button sequences with delays | +| 📋 **Full Logging** | See every USB frame, protocol encode, and device response | ## Supported Devices -| Device | VID:PID | Interface | Buy | -|--------|---------|-----------|-----| -| **Tiqiaa** | `10C4:8468` | pyusb (bulk transfers) | [AliExpress](https://www.aliexpress.com/w/wholesale-tiqiaa-ir-blaster.html) ~$5 | -| **Ocrustar / ElkSmart** | `045C:02AA` + 10 others | pyusb (libusb) | [AliExpress](https://www.aliexpress.com/w/wholesale-usb-ir-blaster.html) ~$3 | +| Device | VID:PID | Interface | Buy | +| ----------------------- | ----------------------- | ---------------------- | ------------------------------------------------------------------------------- | +| **Tiqiaa** | `10C4:8468` | pyusb (bulk transfers) | [AliExpress](https://www.aliexpress.com/w/wholesale-tiqiaa-ir-blaster.html) ~$5 | +| **Ocrustar / ElkSmart** | `045C:02AA` + 10 others | pyusb (libusb) | [AliExpress](https://www.aliexpress.com/w/wholesale-usb-ir-blaster.html) ~$3 | Both devices support **transmit and receive** (learn mode). ## Quick Start ```bash -# 1. Clone git clone https://github.com/deadboy18/iRemote.git cd iRemote - -# 2. Install dependencies pip install -r requirements.txt - -# 3. Run python server.py - -# 4. Open any browser -# http://localhost:7890 ``` -### Windows Extra Step +Then open **http://localhost:7890** in any browser. + +### Platform notes -For **Ocrustar**: install [Zadig](https://zadig.akeo.ie/), select your device (`SMART`), and replace the driver with **WinUSB**. +**macOS** — install libusb: -For **Tiqiaa**: also needs WinUSB via Zadig (despite being HID — it uses bulk transfers internally). +```bash +brew install libusb +``` -### Linux +**Linux** — install libusb and grant USB access: ```bash -# Allow non-root USB access +sudo apt install libusb-1.0-0 # if not installed sudo cp 99-irblaster.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules ``` @@ -99,16 +96,19 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="10c4", ATTR{idProduct}=="8468", MODE="0666" # Ocrustar SUBSYSTEM=="usb", ATTR{idVendor}=="045c", MODE="0666" ``` + +**Windows** — install [Zadig](https://zadig.akeo.ie/), select your device, and replace the driver with **WinUSB** (required for both Ocrustar and Tiqiaa). + ## Screenshots -| Remote | Hotel TV | IR Database | -|--------|----------|-------------| +| Remote | Hotel TV | IR Database | +| --------------------------------- | --------------------------------------- | --------------------------------------------- | | ![Remote](screenshots/Remote.png) | ![Hotel TV](screenshots/Hotel%20Tv.png) | ![IR Database](screenshots/IR%20Database.png) | -| File Import | Learn Mode | Log | -|-------------|------------|-----| +| File Import | Learn Mode | Log | +| --------------------------------------------- | ------------------------------------------- | --------------------------- | | ![File Import](screenshots/File%20Import.png) | ![Learn Mode](screenshots/Learn%20Mode.png) | ![Log](screenshots/Log.png) | ## How It Works @@ -140,25 +140,25 @@ The UI is hosted on [GitHub Pages](https://deadboy18.github.io/iRemote/) so you Built-in sequences for accessing hospitality TV service menus: -| Brand | Macros | -|-------|--------| -| **Samsung** | Hotel Menu, Hotel Menu (Alt), Smart Remote Unlock, Service Menu, Factory Service, PIN Reset | -| **LG** | Installer Menu (9876), Installer Menu (1105), Service Menu | -| **Philips** | Hotel Mode (BDS), Service Menu (SAM) | -| **Sony** | Service Menu | -| **Universal** | Power Off All (6 brands at once) | +| Brand | Macros | +| ------------- | ------------------------------------------------------------------------------------------- | +| **Samsung** | Hotel Menu, Hotel Menu (Alt), Smart Remote Unlock, Service Menu, Factory Service, PIN Reset | +| **LG** | Installer Menu (9876), Installer Menu (1105), Service Menu | +| **Philips** | Hotel Mode (BDS), Service Menu (SAM) | +| **Sony** | Service Menu | +| **Universal** | Power Off All (6 brands at once) | You can also build and save custom macros with the macro builder. ## File Format Support -| Format | Source | Extension | -|--------|--------|-----------| -| Flipper Zero IR | [Flipper-IRDB](https://github.com/Lucaslhm/Flipper-IRDB) | `.ir` | -| IRDB CSV | [probonopd/irdb](https://github.com/probonopd/irdb) | `.csv` | -| iRemote Android JSON | iRemote Android app | `.json` | -| Pronto Hex | Various | `.txt` | -| Raw Pulse Data | Any | `.txt` | +| Format | Source | Extension | +| -------------------- | -------------------------------------------------------- | --------- | +| Flipper Zero IR | [Flipper-IRDB](https://github.com/Lucaslhm/Flipper-IRDB) | `.ir` | +| IRDB CSV | [probonopd/irdb](https://github.com/probonopd/irdb) | `.csv` | +| iRemote Android JSON | iRemote Android app | `.json` | +| Pronto Hex | Various | `.txt` | +| Raw Pulse Data | Any | `.txt` | ## Dependencies @@ -196,4 +196,4 @@ MIT

Made by deadboy18
Protocol reverse engineering, USB drivers, and web interface -

\ No newline at end of file +

diff --git a/server.py b/server.py index 4c84651..fa48c0b 100644 --- a/server.py +++ b/server.py @@ -16,20 +16,26 @@ from flask import Flask, send_from_directory, send_file from flask_socketio import SocketIO, emit except ImportError: - print("ERROR: pip install flask flask-socketio"); sys.exit(1) + print("ERROR: Missing Python web packages. Run: pip install flask flask-socketio") + sys.exit(1) + +try: + import usb.core, usb.util, usb.backend.libusb1 +except ImportError: + print("ERROR: Missing Python USB packages. Run: pip install pyusb libusb-package") + sys.exit(1) -usb_available = False usb_backend = None try: - import usb.core, usb.util - try: - import libusb_package; usb_backend = libusb_package.get_libusb1_backend() - print("[OK] libusb-package backend") - except ImportError: - print("[OK] system libusb") - usb_available = True + import libusb_package; usb_backend = libusb_package.get_libusb1_backend() except ImportError: - print("ERROR: pip install pyusb libusb-package") + pass +if usb_backend is None: + usb_backend = usb.backend.libusb1.get_backend() +if usb_backend is None: + print("ERROR: libusb not found. Run: brew install libusb (macOS) or apt install libusb-1.0-0 (Linux)") + sys.exit(1) +print("[OK] USB ready") app = Flask(__name__, static_folder='.') app.config['SECRET_KEY'] = 'iremote' @@ -276,7 +282,6 @@ def parse_learn(raw): # ═══════════════════════════════════════════════════ def list_usb_devices(): - if not usb_available: return [] devices = [] try: for dev in usb.core.find(find_all=True, backend=usb_backend): @@ -300,7 +305,7 @@ def serve_static(filename): return send_from_directory('.', filename) @socketio.on('connect') def on_connect(): emit('status', {'connected': connected_device is not None, 'type': device_type, - 'variant': ocrustar_variant, 'usb_available': usb_available}) + 'variant': ocrustar_variant}) @socketio.on('list_usb') def on_list_usb(): @@ -312,7 +317,6 @@ def on_list_usb(): @socketio.on('connect_tiqiaa') def on_connect_tiqiaa(): global connected_device, device_type, tiq_cmd_id, tiq_pkt_idx - if not usb_available: emit('error', {'msg': 'pyusb not installed'}); return with device_lock: dev = usb.core.find(idVendor=TIQ_VID, idProduct=TIQ_PID, backend=usb_backend) if not dev: @@ -369,7 +373,6 @@ def on_connect_tiqiaa(): def on_connect_ocrustar(): """Mirrors elksmart_v15.py Dev.connect() + Dev.handshake() exactly.""" global connected_device, device_type, ocrustar_variant, ep_in, ep_out - if not usb_available: emit('error', {'msg': 'pyusb not installed'}); return with device_lock: dev = None; found_pid = None for pid in OCRU_PIDS: @@ -570,15 +573,13 @@ def on_learn(data=None): print("="*52) print(" iRemote Server — USB IR Blaster Backend") print("="*52) - print(f" pyusb: {'OK' if usb_available else 'NOT INSTALLED'}") - if usb_available: - devs = list_usb_devices() - if devs: - print(f" {len(devs)} USB devices:") - for d in devs: - m = '' - if d['vid']==f'0x{OCRU_VID:04X}': m=' ← OCRUSTAR' - if d['vid']==f'0x{TIQ_VID:04X}' and d['pid']==f'0x{TIQ_PID:04X}': m=' ← TIQIAA' - print(f" {d['vid']}:{d['pid']} — {d['product']}{m}") + devs = list_usb_devices() + if devs: + print(f" {len(devs)} USB devices:") + for d in devs: + m = '' + if d['vid']==f'0x{OCRU_VID:04X}': m=' ← OCRUSTAR' + if d['vid']==f'0x{TIQ_VID:04X}' and d['pid']==f'0x{TIQ_PID:04X}': m=' ← TIQIAA' + print(f" {d['vid']}:{d['pid']} — {d['product']}{m}") print(f"\n http://localhost:7890\n{'='*52}") socketio.run(app, host='0.0.0.0', port=7890, debug=False, allow_unsafe_werkzeug=True)