jessikitty bbc4f0878c Add CLI client with refined LED sequence and JSON color config
- Tag detection turns ALL LEDs off immediately
- Server success shows CENTER blue blink (150ms)
- Theme effects (pulse/flash/solid) apply to ALL pads
- External tag_colors.json for customizable per-tag themes
- Debounce logic preserves theme effect even when skipping server
2026-01-26 21:32:54 +11:00
2026-01-24 10:37:03 +11:00
2026-01-24 10:35:16 +11:00

LEGO Dimensions NFC Portal Reader

Python module for reading LEGO Dimensions character and vehicle discs using the USB portal (PS3/PS4/Wii U versions) on Windows.

Designed to integrate with the Moonlight Drive-In video player system.

Features

  • Event-driven tag detection - Callbacks for tag insert/remove events
  • LED control - Set colors, flash, and fade effects on portal pads
  • Thread-safe - Background polling thread with proper locking
  • Character database - Built-in lookup for 80+ characters and vehicles
  • TEA encryption - Full encryption/decryption support for character IDs

Hardware Requirements

Portal Version Compatible
PS3 Yes
PS4 Yes
Wii U Yes
Xbox 360 No
Xbox One No

USB Identifiers:

  • Vendor ID: 0x0e6f
  • Product ID: 0x0241

Installation

1. Install Python Package

pip install pyusb

2. Install Windows USB Driver (Required)

The portal's default Windows HID driver must be replaced with WinUSB:

  1. Download Zadig from https://zadig.akeo.ie/
  2. Connect the LEGO Dimensions portal via USB
  3. Run Zadig as Administrator
  4. Select Options → List All Devices
  5. Choose "LEGO READER V2.10" from the dropdown
  6. Select WinUSB as the target driver
  7. Click "Replace Driver"
  8. Unplug and reconnect the portal

Quick Start

from lego_dimensions_reader import LegoDimensionsReader, COLORS

def on_tag_placed(tag):
    print(f"Tag detected: {tag.uid_hex} on {tag.pad.name}")

def on_tag_removed(tag):
    print(f"Tag removed from {tag.pad.name}")

reader = LegoDimensionsReader()
reader.on_tag_insert = on_tag_placed
reader.on_tag_remove = on_tag_removed

reader.start()

# Keep running...
import time
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    reader.disconnect()

API Reference

LegoDimensionsReader

Main class for portal communication.

Callbacks

Callback Signature Description
on_tag_insert (TagInfo) -> None Called when tag is placed
on_tag_remove (TagInfo) -> None Called when tag is removed
on_connect () -> None Called on successful connection
on_disconnect () -> None Called on disconnection
on_error (Exception) -> None Called on errors

Methods

reader.connect()           # Connect to portal (called automatically by start())
reader.disconnect()        # Disconnect and cleanup
reader.start()             # Start event polling thread
reader.stop()              # Stop polling thread

# LED Control
reader.set_pad_color(Pad.CENTER, COLORS['RED'])
reader.flash_pad(Pad.LEFT, COLORS['GREEN'], on_time=10, off_time=10, count=5)
reader.fade_pad(Pad.RIGHT, COLORS['BLUE'], speed=10, count=1)

# State
reader.get_active_tags()   # Dict of tags currently on pads
reader.is_connected        # Boolean
reader.is_running          # Boolean

TagInfo

Data class returned in callbacks.

Property Type Description
uid bytes 7-byte tag UID
uid_hex str UID as hex string
pad Pad Which pad (CENTER, LEFT, RIGHT)
event TagEvent INSERTED or REMOVED

Pad Enum

from lego_dimensions_reader import Pad

Pad.ALL     # All pads
Pad.CENTER  # Center pad
Pad.LEFT    # Left pad
Pad.RIGHT   # Right pad

Colors

from lego_dimensions_reader import COLORS

COLORS['OFF']      # [0, 0, 0]
COLORS['RED']      # [255, 0, 0]
COLORS['GREEN']    # [0, 255, 0]
COLORS['BLUE']     # [0, 0, 255]
COLORS['WHITE']    # [255, 255, 255]
COLORS['YELLOW']   # [255, 255, 0]
COLORS['CYAN']     # [0, 255, 255]
COLORS['MAGENTA']  # [255, 0, 255]
COLORS['ORANGE']   # [255, 128, 0]
COLORS['PURPLE']   # [128, 0, 255]

Integration with Moonlight Drive-In

Example integration for triggering video playback:

import requests
from lego_dimensions_reader import LegoDimensionsReader

# Map tag UIDs to video files
VIDEO_MAPPING = {
    "04A1B2C3D4E5F6": "videos/batman_intro.mp4",
    "04D5E6F7A8B9C0": "videos/gandalf_intro.mp4",
}

MOONLIGHT_API = "http://drive-in:8547"

def on_tag_insert(tag):
    video = VIDEO_MAPPING.get(tag.uid_hex)
    if video:
        requests.post(f"{MOONLIGHT_API}/api/play", json={"path": video})

def on_tag_remove(tag):
    requests.post(f"{MOONLIGHT_API}/api/stop")

reader = LegoDimensionsReader()
reader.on_tag_insert = on_tag_insert
reader.on_tag_remove = on_tag_remove
reader.start()

TEA Encryption Functions

For advanced tag reading/writing:

from lego_dimensions_reader import (
    generate_password,
    generate_tea_key,
    decrypt_character_id,
    encrypt_character_id
)

uid = bytes.fromhex("04A1B2C3D4E5F6")

# Generate tag password
password = generate_password(uid)

# Decrypt character ID from tag data
encrypted_data = bytes.fromhex("...")  # 8 bytes from pages 0x24-0x25
character_id = decrypt_character_id(uid, encrypted_data)

# Encrypt character ID for writing
encrypted = encrypt_character_id(uid, character_id=1)  # Batman

Troubleshooting

"LEGO Dimensions Portal not found"

  • Ensure portal is connected via USB
  • Check you're using PS3/PS4/Wii U portal (NOT Xbox)
  • Verify Zadig driver installation

"Access denied" error

  • Run your script as Administrator
  • Re-run Zadig driver installation

Portal detected but no tag events

  • Try unplugging and reconnecting portal
  • Check USB cable connection
  • Ensure tag is placed flat on the pad

Technical Details

NFC Tags

  • Tag type: NTAG213 (Mifare Ultralight C)
  • Memory: 144 bytes (45 pages × 4 bytes)
  • Character data: Pages 0x24-0x25 (TEA encrypted)
  • Vehicle data: Page 0x24 (unencrypted)
  • Password protection: Pages 0x24+

USB Protocol

  • Packet size: 32 bytes (fixed)
  • Endpoint OUT: 0x01
  • Endpoint IN: 0x81
  • Tag events start with byte 0x56

Credits

Based on reverse-engineering work by:

  • ags131 (node-ld)
  • bettse
  • socram8888
  • Ellerbach (LegoDimensions .NET library)

License

MIT License

Description
LEGO Dimensions NFC Portal reader module for Python on Windows - integrates with Moonlight Drive-In video player system
Readme 51 KiB
Languages
Python 100%