Metadata-Version: 2.4
Name: pywiim
Version: 3.0.1
Summary: Python library for WiiM/LinkPlay device communication
Author-email: Michael Cumming <mjcumming@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/mjcumming/pywiim
Project-URL: Repository, https://github.com/mjcumming/pywiim
Project-URL: Issues, https://github.com/mjcumming/pywiim/issues
Keywords: wiim,linkplay,audio,upnp,streaming,music,async
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.8.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: async-upnp-client>=0.40.0
Requires-Dist: m3u8>=3.5.0
Requires-Dist: mutagen>=1.47.0
Provides-Extra: mcp
Requires-Dist: mcp[cli]>=1.0.0; extra == "mcp"
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
Requires-Dist: pyyaml>=6.0.0; extra == "dev"
Dynamic: license-file

# pywiim

Python library for WiiM and LinkPlay device control with command-line tools for discovery, diagnostics, and monitoring.

[![CI](https://github.com/mjcumming/pywiim/actions/workflows/ci.yml/badge.svg)](https://github.com/mjcumming/pywiim/actions/workflows/ci.yml) [![Security](https://github.com/mjcumming/pywiim/actions/workflows/security.yml/badge.svg)](https://github.com/mjcumming/pywiim/actions/workflows/security.yml) [![codecov](https://codecov.io/gh/mjcumming/pywiim/branch/main/graph/badge.svg)](https://codecov.io/gh/mjcumming/pywiim) [![PyPI version](https://img.shields.io/pypi/v/pywiim)](https://pypi.org/project/pywiim/) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Type checking: mypy](https://img.shields.io/badge/type%20checking-mypy-blue.svg)](https://mypy.readthedocs.io/) [![Linting: ruff](https://img.shields.io/badge/linting-ruff-yellow.svg)](https://github.com/astral-sh/ruff)

## Overview

`pywiim` is an asynchronous Python library for controlling WiiM and other LinkPlay-based audio devices. It combines device APIs and UPnP with capability detection, source-aware state, multiroom support, and command-line tools. It powers the [WiiM Home Assistant integration](https://github.com/mjcumming/wiim) and can be used directly in Python applications.

## Key Features

- **Playback and queues** — Control playback, volume, mute, source selection, favorites, and supported queues.
- **Multiroom audio** — Create, join, and leave groups, with group-aware playback and volume controls.
- **Graphic and parametric EQ** — Track EQ per input, distinguish saved presets, unsaved curves, Off, and unknown, and verify supported band and preset changes by reading the device back. Compatible WiiM devices also support PEQ profiles and per-band editing.
- **Device state and events** — Combine HTTP polling with optional UPnP events; expose device information, playback state, source capabilities, and audio outputs through `Player`.
- **Sources and audio outputs** — Discover model-specific inputs and outputs, including supported Bluetooth, analog, optical, USB, HDMI, and streaming sources. `player.source_catalog` provides structured source information for integrations.
- **Discovery and diagnostics** — Find devices with SSDP/UPnP or network scanning, inspect capabilities, and monitor or verify devices with the included command-line tools.
- **More device controls** — Use supported alarms, sleep timers, firmware information, and other model-dependent settings.

WiiM, Audio Pro, Arylic, and other LinkPlay-based devices differ by model, firmware, and input. The library detects capabilities where possible; a feature listed above is not available on every device. PEQ is supported on compatible WiiM devices.

## Installation

Install `pywiim` to use the command-line tools for discovering, testing, and monitoring your WiiM/LinkPlay devices, or to use the Python library in your projects.

### Prerequisites

- Python 3.11 or later
- pip (usually included with Python)

**Installing Python:**
- **Linux/macOS**: Usually pre-installed. If not, use your package manager or download from [python.org](https://www.python.org/downloads/)
- **Windows**: Download from [python.org](https://www.python.org/downloads/) and check "Add Python to PATH" during installation

### Install pywiim

```bash
pip install pywiim
```

The CLI tools (`wiim-discover`, `wiim-diagnostics`, `wiim-monitor`, `wiim-verify`) are automatically installed and available in your PATH.

**Verify installation:**
```bash
wiim-discover --help
```

**Note for Windows users:** If the commands are not found after installation, ensure Python's Scripts directory is in your PATH (usually `C:\Users\YourName\AppData\Local\Programs\Python\Python3X\Scripts`), or restart your terminal.

## Quick start: Python

Create a client for the device, then use `Player` for its cached state and controls. Replace the example address with your device's IP or hostname.

```python
import asyncio

from pywiim import Player, WiiMClient


async def main():
    client = WiiMClient("192.168.1.100")
    player = Player(client)
    try:
        await player.refresh(full=True)
        print(f"Device: {player.name} ({player.model_name or player.model})")
        print(f"Playing: {player.play_state}; volume: {player.volume_level}")
        print(f"Input: {player.source}; graphic EQ: {player.eq_preset}")
    finally:
        await client.close()


asyncio.run(main())
```

`player.eq_preset` can be a device-reported preset, `Custom` for an unsaved graphic curve, `Off`, or `None` when unknown. `player.peq_enabled` reports the separate parametric EQ state when supported. See the [API reference](docs/integration/API_REFERENCE.md) for playback, grouping, and [EQ and PEQ examples](docs/integration/API_REFERENCE.md#parametric-eq-peq--wiim-only), including band writes and saved profiles.

### Upgrading from 2.x

Version 3.0 makes unknown EQ status explicit: `get_eq_status()` can return `None`, and invalid preset-list responses raise an error. `set_eq_custom()` takes ten gains in dB (-12 to +12), while replacing a saved PEQ profile requires `overwrite=True`. PEQ and RoomFit reads omit incomplete bands rather than filling in default values. See the [3.0.0 changelog](CHANGELOG.md) for the complete compatibility notes.

## Command-Line Tools

The library includes four powerful CLI tools that are automatically installed with `pywiim`. These tools provide an easy way to discover, diagnose, monitor, and test your WiiM/LinkPlay devices without writing any code.

### Quick Start

1. **Discover devices on your network:**
   ```bash
   wiim-discover
   ```

2. **Test a device** (this can interrupt playback and change settings; replace `192.168.1.100` with your device IP):
   ```bash
   wiim-verify 192.168.1.100
   ```

3. **Monitor a device in real-time:**
   ```bash
   wiim-monitor 192.168.1.100
   ```

4. **Run diagnostics:**
   ```bash
   wiim-diagnostics 192.168.1.100
   ```

### 1. Device Discovery (`wiim-discover`)

Discover all WiiM/LinkPlay devices on your network using SSDP/UPnP or network scanning.

**What it does:**
- Automatically finds all WiiM and LinkPlay-based devices on your local network
- Validates discovered devices by testing their API
- Displays device information (name, model, firmware, IP, MAC, UUID)
- Supports multiple discovery methods for maximum compatibility

**Usage:**
```bash
# Basic discovery (SSDP/UPnP)
wiim-discover

# Output as JSON (useful for scripting)
wiim-discover --output json

# Skip API validation (faster, less detailed)
wiim-discover --no-validate

# Verbose logging
wiim-discover --verbose

# Custom SSDP timeout
wiim-discover --ssdp-timeout 10
```

**Options:**
- `--ssdp-timeout <seconds>` - SSDP discovery timeout (default: 5)
- `--no-validate` - Skip API validation of discovered devices
- `--output <text|json>` - Output format (default: text)
- `--verbose, -v` - Enable verbose logging

**Example Output:**
```
🔍 Discovering WiiM/LinkPlay devices via SSDP...

Device: WiiM Mini
  IP Address: 192.168.1.100:80
  Protocol: HTTP
  Model: WiiM Mini
  Firmware: 4.8.123456
  MAC Address: AA:BB:CC:DD:EE:FF
  UUID: 12345678-1234-1234-1234-123456789abc
  Vendor: WiiM
  Discovered via: SSDP
  Status: Validated ✓
```

See [Discovery Documentation](docs/user/DISCOVERY.md) for more information.

### 2. Diagnostic Tool (`wiim-diagnostics`)

Comprehensive diagnostic tool for troubleshooting device issues and gathering information for support.

**What it does:**
- Gathers complete device information (model, firmware, MAC, UUID, capabilities)
- Includes UPnP `description.xml` capability enrichment (advertised services like PlayQueue/QPlay)
- Tests all API endpoints to verify functionality
- Tests feature support (presets, EQ, multiroom, Bluetooth, etc.)
- Shows the reported input, graphic and parametric EQ state, available presets, and reported band values
- Generates detailed JSON reports for sharing with developers
- Identifies errors and warnings

**Usage:**
```bash
# Basic diagnostic
wiim-diagnostics 192.168.1.100

# Save report to file (for sharing with support)
wiim-diagnostics 192.168.1.100 --output report.json

# HTTPS device
wiim-diagnostics 192.168.1.100 --port 443

# Verbose output
wiim-diagnostics 192.168.1.100 --verbose
```

**Options:**
- `<device_ip>` - Device IP address or hostname (required)
- `--port <port>` - Device port (default: 80, use 443 for HTTPS)
- `--output <file>` - Save report to JSON file
- `--verbose` - Enable detailed logging

**What it tests:**
- Device information retrieval
- Capability detection
- All status endpoints
- Feature support detection
- API endpoint availability
- Error conditions

**Example Output:**
```
🔍 Starting comprehensive device diagnostic...
   Device: 192.168.1.100:80

📋 Gathering device information...
   ✓ Device: WiiM Mini (WiiM Mini)
   ✓ Firmware: 4.8.123456
   ✓ MAC: AA:BB:CC:DD:EE:FF

🔧 Detecting device capabilities...
   ✓ Vendor: WiiM
   ✓ Device Type: WiiM
   ✓ Supports EQ: Yes
   ✓ Supports Presets: Yes
   ...
```

See [Diagnostics Documentation](docs/user/DIAGNOSTICS.md) for more information.

### 3. Real-time Monitor (`wiim-monitor`)

Monitor your device in real-time with adaptive polling and UPnP event support.

**What it does:**
- Displays live device status with automatic updates
- Uses UPnP events for instant updates when available
- Falls back to adaptive HTTP polling
- Shows play state, volume, mute, track info, and playback position
- Shows the current input's graphic EQ and PEQ state, including Custom and unknown
- Displays device role in multiroom groups
- Tracks statistics (poll count, state changes, UPnP events)

**Usage:**
```bash
# Basic monitoring
wiim-monitor 192.168.1.100

# Specify callback host for UPnP (if auto-detection fails)
wiim-monitor 192.168.1.100 --callback-host 192.168.1.254

# Verbose logging
wiim-monitor 192.168.1.100 --verbose

# Custom log level
wiim-monitor 192.168.1.100 --log-level DEBUG

# Verbose UPnP event logging (shows full event JSON/XML)
wiim-monitor 192.168.1.100 --upnp-verbose
```

**Options:**
- `<device_ip>` - Device IP address or hostname (required)
- `--callback-host <ip>` - Override UPnP callback host (auto-detected by default)
- `--verbose, -v` - Enable verbose logging
- `--log-level <level>` - Set log level (DEBUG, INFO, WARNING, ERROR)
- `--upnp-verbose` - Enable verbose UPnP event logging (shows full event JSON/XML data)

**What it displays:**
- Play state (playing, paused, stopped)
- Volume level and mute status
- Current track (title, artist, album)
- Playback position and duration
- Device role (solo/master/slave)
- Group information (if in a group)
- Update source (polling or UPnP event)
- Statistics on exit

**Example Output:**
```
🎵 Monitoring WiiM Mini (192.168.1.100)...
   UPnP: Enabled ✓ (events: 0)
   Polling: Adaptive (interval: 2.0s)

📊 Status:
   State: playing
   Volume: 50% (muted: No)
   Source: wifi
   Role: solo

🎶 Track:
   Title: Song Title
   Artist: Artist Name
   Album: Album Name
   Position: 1:23 / 3:45

[UPnP] State changed: volume → 55%
```

Press `Ctrl+C` to stop monitoring and view statistics.

### 4. Feature Verification (`wiim-verify`)

Comprehensive testing tool that verifies all device features and endpoints with safety constraints.

**What it does:**
- Tests all playback controls (play, pause, stop, next, previous)
- Tests volume controls (safely, never exceeds 10%)
- Tests source switching
- Tests audio output modes
- Tests EQ controls (if supported)
- Tests group operations (if applicable)
- Tests preset playback
- Tests all status endpoints
- Saves and restores original device state
- Generates detailed test report

**Usage:**
```bash
# Basic verification
wiim-verify 192.168.1.100

# Verbose output (shows detailed test data)
wiim-verify 192.168.1.100 --verbose

# HTTPS device
wiim-verify 192.168.1.100 --port 443
```

**Options:**
- `<device_ip>` - Device IP address or hostname (required)
- `--port <port>` - Device port (default: 80, use 443 for HTTPS)
- `--verbose, -v` - Enable verbose output (shows detailed test data)

**Before running:** The verifier changes playback, volume, sources, and output settings, so it can interrupt normal use. It attempts to restore the original volume, mute, and source, but complete restoration is not guaranteed. EQ checks are read-only to avoid overwriting custom curves.

**What it tests:**
- Status endpoints (get_player_status, get_device_info, etc.)
- Playback controls (play, pause, resume, stop, next, previous)
- Volume controls (set_volume, set_mute)
- Source controls (set_source, get_source)
- Audio output controls (set_audio_output_mode)
- EQ reads (get_eq, get_eq_presets, get_eq_status, and supported advanced EQ reads)
- Multiroom status and group membership reads
- Preset operations (play_preset)
- And more...

**Example Output:**
```
💾 Saving original device state...
   ✓ Volume: 0.5
   ✓ Mute: False
   ✓ Source: wifi
   ✓ Play state: playing

📊 Testing Status Endpoints...
   ✓ get_player_status
   ✓ get_player_status_model
   ✓ get_meta_info

▶️  Testing Playback Controls...
   ✓ play
   ✓ pause
   ✓ resume
   ✓ stop
   ✓ next_track
   ✓ previous_track

🔊 Testing Volume Controls (max 10%)...
   ✓ set_volume (5%)
   ✓ set_volume (10%)
   ✓ set_mute (True)
   ✓ set_mute (False)

...

🔄 Restoring original device state...
   ✓ Volume restored
   ✓ Mute restored
   ✓ Source restored

============================================================
Total tests: 45
✅ Passed: 42
❌ Failed: 0
⊘ Skipped: 3
```

**Exit Codes:**
- `0` - All tests passed
- `1` - One or more tests failed or interrupted

## Notification Playback Limitations

`play_notification()` uses a source-aware strategy so notifications are audible across more real-world scenarios:

- **Prompt path** (`playPromptUrl`) on device-controlled sources when the library considers them "native" (e.g. wifi, tunein, USB). When firmware supports it, the device can duck, play the prompt, and resume.
- **Fallback path** (`play_url`) on unsupported or unknown sources so the notification is always heard (interrupts current playback).
- **`force_interrupt=True`** — use for TTS when the prompt path is unreliable: always uses `play_url` so the notification is guaranteed audible.
- Returned result object reports what happened: `method_used`, `source_before`, `likely_interrupted`, `reason`.

### Why fallback exists

Prompt behavior (duck, play, resume) is **firmware- and source-dependent**. Some devices/sources return OK for `playPromptUrl` but do not play the prompt or do not resume. In those cases the library falls back to `play_url` so the notification is heard.

### Practical limitations

- Fallback playback is interruptive by design and may stop/replace the previous source session.
- Unknown/unmapped sources default to fallback for reliability.
- Do not overclaim duck/resume in integration release notes — behavior varies by device and firmware.

## Documentation

### User Guides
- [Discovery Guide](docs/user/DISCOVERY.md) - Device discovery via SSDP/UPnP
- [Diagnostics Guide](docs/user/DIAGNOSTICS.md) - Using the diagnostic tool
- [Real-time Monitor Guide](docs/user/MONITOR.md) - Real-time device monitoring

### Integration Guides
- [Home Assistant Integration](docs/integration/HA_INTEGRATION.md) - Complete guide for HA integrations
  - DataUpdateCoordinator patterns
  - Adaptive polling strategies
  - UPnP event integration
  - Queue management
  - Source-aware shuffle/repeat control
- [API Reference](docs/integration/API_REFERENCE.md) - Complete API documentation

### Design Documentation
- [Architecture & Data Flow](docs/design/ARCHITECTURE_DATA_FLOW.md) - System architecture
- [State Management](docs/design/STATE_MANAGEMENT.md) - State synchronization patterns
- [Operation Patterns](docs/design/OPERATION_PATTERNS.md) - Common operation patterns
- [LinkPlay Architecture](docs/design/LINKPLAY_ARCHITECTURE.md) - **In-depth analysis of LinkPlay/WiiM streaming architecture**
  - "Split Brain" control authority model
  - Transport protocol analysis (AirPlay, Spotify, USB, Bluetooth)
  - Hardware constraints (A98 SoM, RAM limits, queue management)
  - Why shuffle/repeat controls work differently for different sources
  - Integration strategies for automation systems

## Optional MCP server (`wiim-mcp`)

The optional [Model Context Protocol](https://modelcontextprotocol.io) server exposes device controls to compatible MCP hosts.

```bash
pip install 'pywiim[mcp]'
wiim-mcp
```

For Cursor or Claude Desktop, add the server to the host's MCP configuration:

```json
{
  "mcpServers": {
    "wiim": {
      "command": "wiim-mcp",
      "args": []
    }
  }
}
```

The server reads `~/.config/wiim/config.json` by default; `WIIM_CONFIG_FILE` changes the path. Start with the [example configuration](pywiim/mcp/config.example.json) and add your device addresses. The file supports `default_device`, `named_devices`, `discovery_disabled`, `timeout`, and `discovery_timeout`. Environment variables `WIIM_DEFAULT_DEVICE`, `WIIM_NAMED_DEVICES` (JSON), `WIIM_DISCOVERY_DISABLED`, `WIIM_TIMEOUT`, and `WIIM_DISCOVERY_TIMEOUT` override the file. If discovery is unavailable, such as in some WSL setups, configure named devices and set `discovery_disabled` to `true`.

Available tools cover discovery, status, playback, volume, mute, source selection, URL playback, and group join/leave.

## Development Setup

See [SETUP.md](SETUP.md) for detailed development setup instructions.

Quick start:
```bash
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/unit/ -v

# Run code quality checks
make lint typecheck
```

## Acknowledgments

This library was made possible by the work of many developers who have reverse-engineered and documented the WiiM/LinkPlay API. We would like to acknowledge the following projects and resources that provided valuable API information and implementation insights:

### Libraries and Implementations
- **[python-linkplay](https://pypi.org/project/python-linkplay/)** - Python library for LinkPlay devices that provided insights into state detection and API patterns (enhanced state detection logic from v0.2.9)
- **[linkplay-cli](https://github.com/ramikg/linkplay-cli)** - Command-line tool for LinkPlay devices (provided SSL certificate reference for Audio Pro devices)
- **[WiiM HTTP API OpenAPI Specification](https://github.com/cvdlinden/wiim-httpapi)** - Comprehensive OpenAPI 3.0 specification for WiiM HTTP API endpoints
- **[Home Assistant WiiM Integration](https://github.com/mjcumming/wiim)** - Production-tested implementation that informed many design decisions, polling strategies, and state management patterns
- **[WiiM Play](https://github.com/shumatech/wiimplay)** - UPnP-based implementation that provided UPnP integration insights
- **[Velleman python-linkplay](https://github.com/Velleman/python-linkplay)** - Provided valuable API information and patterns for LinkPlay device communication
- **[Home Assistant LinkPlay Custom Component](https://github.com/nagyrobi/home-assistant-custom-components-linkplay)** - Custom Home Assistant integration for LinkPlay devices
- **[LinkPlay A31 Alternative Firmware](https://github.com/hn/linkplay-a31)** - Alternative firmware project that provided insights into LinkPlay hardware capabilities

### Official Documentation
- [Arylic LinkPlay API Documentation](https://developer.arylic.com/httpapi/) - Official LinkPlay protocol documentation
- [WiiM HTTP API PDF](https://www.wiimhome.com/pdf/HTTP%20API%20for%20WiiM%20Products.pdf) - Official WiiM API documentation

### Contributors and Notable Contributions
- **[jeromeof](https://github.com/jeromeof)** – Full Parametric EQ (PEQ) API ([PR #12](https://github.com/mjcumming/pywiim/pull/12)): WiiM LV2 PEQ support (10-band per-source, stereo/L-R channel modes, preset save/load/delete/rename). Implemented with capability probing (`supports_peq`) and integration tests against real devices.

### Additional Resources
- Various GitHub repositories and community contributions that helped document the LinkPlay protocol and WiiM-specific enhancements
- The LinkPlay and WiiM developer communities for sharing API discoveries and reverse-engineering efforts

If you know of other libraries or resources that should be acknowledged, please [open an issue](https://github.com/mjcumming/pywiim/issues) or submit a pull request.

## License

MIT License
