Metadata-Version: 2.4
Name: lovensepy
Version: 1.0.0
Summary: Python Lovense API client
Home-page: https://github.com/koval01/lovensepy
Author: Koval Yaroslav
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: websockets>=12.0
Requires-Dist: hyperframe>=6.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Dynamic: author
Dynamic: home-page
Dynamic: license-file

# LovensePy

Python client for the **Lovense API**. Supports Standard API (LAN & Server), Standard Socket API, and Toy Events API.

## Features

- **Standard API LAN (Game Mode)**: GetToys, GetToyName, Function, Stop, Pattern, Preset, Position, PatternV2
- **Standard API Server**: Function, Pattern, Preset via Lovense cloud; `get_qr_code` for QR pairing
- **Standard Socket API**: getToken, getSocketUrl, WebSocket client for QR flow and remote control
- **Toy Events API**: Real-time events (toy-list, button-down, function-strength-changed, etc.)

## Installation

```bash
pip install lovensepy
```

Dependencies: `httpx`, `pydantic`, `websockets`

---

## API Variants

| API | Client | Auth | Notes |
|-----|--------|------|-------|
| Standard / local | `LANClient` | Game Mode (IP + port) | Lovense Remote: 20011/30011. Connect: 34567 |
| Standard / server | `ServerClient` | token + uid | uid from QR callback. Use `get_qr_code` for pairing |
| Socket / server | `SocketAPIClient` | getToken, getSocketUrl | QR scan, commands via WebSocket |
| Socket / local | `SocketAPIClient(use_local_commands=True)` | same + LAN | Commands via HTTPS to device |
| Socket / local only | `LANClient` | IP + port only | No token, no WebSocket |
| Events API | `ToyEventsClient` | access (appName) | Port 20010. Lovense Remote only |

**Flow:** Standard local → HTTP/HTTPS to device. Standard server → HTTPS to Lovense cloud. Socket → WebSocket to cloud (or HTTPS to device when `use_local_commands=True`). Events → WebSocket to device.

---

## Usage

### Standard API LAN (Game Mode)

Lovense Remote or Connect > Game Mode > Enable LAN. No token, no QR.

```python
from lovensepy import LANClient, Actions, Presets

client = LANClient("My App", "192.168.1.100", port=20011)
print(client.get_toys())
client.function_request({Actions.ALL: 10}, time=5)
client.preset_request(Presets.PULSE, time=5)
client.stop()
```

### Standard API Server

Requires developer token and uid (from QR pairing callback). Use `get_qr_code` to get QR for pairing.

```python
from lovensepy import ServerClient, get_qr_code, Actions

# QR pairing: get_qr_code(token, uid) → user scans → callback receives uid
client = ServerClient(developer_token="...", uid="user_123")
client.function_request({Actions.VIBRATE: 10}, time=5)
```

### Standard Socket API

`platform` must match **Website Name** from Lovense Developer Dashboard.

```python
from lovensepy import get_token, get_socket_url, build_websocket_url, SocketAPIClient

auth_token = get_token(developer_token, uid, uname="User")
socket_info = get_socket_url(auth_token, platform="Your App")
ws_url = build_websocket_url(socket_info, auth_token)
client = SocketAPIClient(ws_url, on_event=lambda e, p: print(e, p))
await client.connect()
# Commands: client.send_command("Function", "Vibrate:10", time_sec=5)
```

**By local** (same LAN): `use_local_commands=True` — commands go via HTTPS to device after QR scan.

### Toy Events API

Real-time events from toys. **Lovense Remote only**, port 20010.

```python
from lovensepy import ToyEventsClient

client = ToyEventsClient("192.168.1.100", port=20010, app_name="My App", on_event=handler)
await client.connect()
```

### High-level patterns

```python
from lovensepy import LANClient, SyncPatternPlayer, features_for_toy

client = LANClient("My App", "192.168.1.100", port=20011)
toys = client.get_toys().get("data", {}).get("toys", {})
player = SyncPatternPlayer(client, toys)
player.play_sine_wave("toy_id", "Vibrate1", duration_sec=5)
player.play_combo([("t1", "Vibrate1"), ("t2", "Vibrate")], duration_sec=4)
player.stop("toy_id")
```

---

## Architecture

- **Clients**: LANClient, ServerClient, SocketAPIClient, ToyEventsClient — command building, protocols
- **Transport**: HttpTransport (POST JSON), WsTransport (WebSocket)
- **Security**: Certificate fingerprint verification for HTTPS (port 30010/30011) when `verify_ssl=False`

---

## HTTPS Certificate

For local HTTPS (ports 30010/30011), lovensepy verifies the Lovense certificate fingerprint instead of disabling SSL. Fingerprint in `lovensepy.security.LOVENSE_HTTPS_FINGERPRINT`.

---

## Examples

- `examples/lan_game_mode.py` — LAN Game Mode
- `examples/patterns_demo.py` — Sine waves, combos
- `examples/server_api.py` — Server API
- `examples/socket_api_full.py` — Socket API with QR
- `examples/toy_events_full.py` — Toy Events

---

## Tests

| Mode | Test | Env Vars |
|------|------|----------|
| Standard / local | `test_standard_local.py` | `LOVENSE_LAN_IP`, `LOVENSE_LAN_PORT` (20011 Remote, 34567 Connect) |
| Standard / server | `test_standard_server.py` | `LOVENSE_DEV_TOKEN`, `LOVENSE_UID` (or `LOVENSE_QR_PAIRING=1` + ngrok) |
| Socket / server | `test_socket_server.py` | `LOVENSE_DEV_TOKEN`, `LOVENSE_UID`, `LOVENSE_PLATFORM` |
| Socket / local | `test_socket_server.py::test_by_local` | Same + same LAN |
| Socket / local only | `test_socket_local.py` | `LOVENSE_LAN_IP`, `LOVENSE_LAN_PORT` |
| Toy Events | `test_toy_events.py` | `LOVENSE_LAN_IP`, `LOVENSE_TOY_EVENTS_PORT` (20010) |

```bash
pip install -e ".[dev]"

# Unit tests (no devices)
pytest tests/test_unit.py -v

# Integration tests — set env vars per table above, then:
export LOVENSE_LAN_IP=192.168.1.100
export LOVENSE_LAN_PORT=34567 # Lovense Connect
export LOVENSE_DEV_TOKEN=your_token
export LOVENSE_UID=your_uid
export LOVENSE_PLATFORM="Your App"
export LOVENSE_TOY_EVENTS_PORT=20011 # Lovense Remote
export LOVENSE_QR_PAIRING=1
export LOVENSE_CALLBACK_PORT=8765 # start cloudflared --url http://localhost:8765

pytest tests/test_standard_local.py -v -s
pytest tests/test_standard_server.py -v -s
pytest tests/test_socket_server.py -v -s
pytest tests/test_socket_local.py -v -s
pytest tests/test_toy_events.py -v -s
```

---

## Links

- [Lovense Standard API](https://developer.lovense.com/docs/standard-solutions/standard-api.html)
- [Lovense Socket API](https://developer.lovense.com/docs/standard-solutions/socket-api.html)
- [Toy Events API](https://developer.lovense.com/docs/standard-solutions/toy-events-api.html)

## License

MIT License.
