This took so much trial and error - and packet sniffing - and help from AI - to figure it out:
You need lovense connect 1.9.0. Run it on your local PC - connect your toys.
Below is a simple program in python to send commands (via a simple UI) - I am sharing it here because I couldn’t find a simple solution like this - I’m no coder, so if this doesn’t work for you then sorry, but I suggest using Grok 4 or something like that to try and get it working for you - but this works for me!
import tkinter as tk
from tkinter import ttk
import requests
import json
import threading
API_BASE = “http://127.0.0.1:20010/command”
HEADERS = {
“Content-Type”: “application/json”,
“X-platform”: “MyTestApp”
}
def get_toys():
try:
res = requests.post(API_BASE, json={“command”: “GetToys”}, headers=HEADERS)
print(“ Raw response text:”, res.text)
res_json = res.json()
toys_json_str = res_json.get(“data”, {}).get(“toys”, “{}”)
toys_data = json.loads(toys_json_str)
return {info.get(“nickName”, toy_id): toy_id for toy_id, info in toys_data.items()}
except Exception as e:
print(f" Error in get_toys(): {e}")
return {}
def vibrate(toy_id, level=10, seconds=5):
body = {
“command”: “Function”,
“action”: f"{control_mode.get()}:{level}“,
“timeSec”: seconds,
“apiVer”: 1,
“toy”: toy_id
}
response = requests.post(API_BASE, json=body, headers=HEADERS)
print(f" Payload sent: {body}”)
print(f" Response: {response.status_code} {response.text}")
return response.json()
def get_battery(toy_id):
body = {“command”: “GetBattery”, “toy”: toy_id, “apiVer”: 1}
response = requests.post(API_BASE, json=body, headers=HEADERS)
print(f" Battery response: {response.text}")
return response.json().get(“data”, “Unknown”)
def send_command():
selected = toy_selector.get()
toy_id = toy_map.get(selected)
level = int(vibration_level.get())
seconds = int(duration.get())
if toy_id:
result = vibrate(toy_id, level, seconds)
status.set(f" Sent: {result.get(‘code’, ‘OK’)} - {result.get(‘message’, ‘Success’)}")
else:
status.set(“ No toy selected”)
def stop_vibrate():
selected = toy_selector.get()
toy_id = toy_map.get(selected)
if toy_id:
result = vibrate(toy_id, 0, 0)
status.set(f" Stopped: {result.get(‘code’, ‘OK’)} - {result.get(‘message’, ‘Success’)}")
else:
status.set(“ No toy selected”)
def refresh():
global toy_map
toy_map = get_toys()
toy_selector[‘values’] = list(toy_map.keys())
if toy_map:
toy_selector.current(0)
status.set(“ Toys loaded”)
else:
status.set(“ No toys found”)
def poll_battery():
while True:
selected = toy_selector.get()
toy_id = toy_map.get(selected)
if toy_id:
batt = get_battery(toy_id)
current_status = status.get().split(" | Battery:“)[0] if " | Battery:” in status.get() else status.get()
status.set(f"{current_status} | Battery: {batt}")
time.sleep(10) # Poll every 10 seconds
root = tk.Tk()
root.title(“Lovense Toy Controller”)
ttk.Label(root, text=“Select Toy:”).grid(row=0, column=0, sticky=“e”)
toy_selector = ttk.Combobox(root, width=30)
toy_selector.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(root, text=“Control Mode:”).grid(row=1, column=0, sticky=“e”)
control_mode = tk.StringVar(value=“Vibrate”)
ttk.Combobox(root, textvariable=control_mode, values=[“Vibrate”, “Rotate”]).grid(row=1, column=1, padx=5, pady=5)
ttk.Label(root, text=“Level (1-20):”).grid(row=2, column=0, sticky=“e”)
vibration_level = tk.Spinbox(root, from_=1, to=20, width=5)
vibration_level.grid(row=2, column=1, sticky=“w”, padx=5)
ttk.Label(root, text=“Duration (s):”).grid(row=3, column=0, sticky=“e”)
duration = tk.Spinbox(root, from_=1, to=60, width=5)
duration.grid(row=3, column=1, sticky=“w”, padx=5)
ttk.Button(root, text=“Send Command”, command=send_command).grid(row=4, column=0, pady=10)
ttk.Button(root, text=“Stop”, command=stop_vibrate).grid(row=4, column=1, pady=10)
ttk.Button(root, text=“Refresh Toys”, command=refresh).grid(row=5, column=0, columnspan=2, pady=5)
status = tk.StringVar()
ttk.Label(root, textvariable=status).grid(row=6, column=0, columnspan=2)
toy_map = {}
refresh()
threading.Thread(target=poll_battery, daemon=True).start()
root.mainloop()