DeveloperDeveloper
  • Standard Solutions
  • Cam Solutions
  • Native SDKs
  • App Gallery
  • Game Engine Plugins
Forum
Projects Library
Dev Discord
  • Standard Solutions
  • Cam Solutions
  • Native SDKs
  • App Gallery
  • Game Engine Plugins
Forum
Projects Library
Dev Discord
  • Standard Solutions (with Lovense Remote)

    • Introduction
    • Standard API
    • Standard Socket API
    • Standard JS SDK
    • Toy Events API
Got Stuck?
Forum for Lovense Developers
Support
Document Feedback

Toy Events API

Introduction

The Toy Events API is a WebSocket-based API that enables Lovense's partners to receive real-time events from Lovense sex toys.

TIP

The API is available in Lovense Remote iOS version v6.1.6 or later and Lovense Remote Android version v6.2.1 or later.

How to use Toy Events API

1. Get websocket server address

1.1 Open Lovense Remote and enable game mode

Lovense Remote Menu

1.2 Get IP and port

  • Method A:

    From Lovense Remote Screen

    Lovense Remote game mode

  • Method B: Find your user's toy(s)

2. Connect to websocket server

Example:

// create websocket client
using UnityEngine;
namespace UnityWebSocket.Demo
{
    public Class UnityWebSocketDemo: MonoBehaviour
    {
		public string address = "ws://xxx.xxx.xxx.xxx:xxxxx/v1";

        private IWebSocket socket;

        private void OnGUI()
        {
            WebSocketState state = socket == null ? WebSocketState.Closed : socket.ReadyState;
            Socket_Connection(socket);
        }
        private void Socket_Connection(IWebSocket socket)
        {
            socket = new WebSocket(address);
            socket.OnOpen += Socket_onOpen;
            socket.OnMessage += Socket_onMessage;
            socket.OnClose += Socket_OnClose;
            socket.OnError += Socket_OnError;
            socket.ConnectAsync();
        }
        //Socket_onOpen
        //Socket_OnClose
        //Socket_OnError
        //Socket_onMessage
    }
}
const ws = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxxx/v1")

3. Listen for events

Example:

// Listen for events
[System.Serializable]
public class PlayerInfo{
    private string type;
    private T data;
    ...
}

private void Socket_OnMessage(object sender, MesageEventArgs e)
{
    if(e.IsBinary){
        Logger.Log("Receive Bytes({1}): {0}", e.Data, e.RawData.Length);
    }else if(e.IsText){
        Logger.Log("Receive Text: {0}", e.Data);
        PlayerInfo info = JsonUtility.FromJson<PlayerInfo>(e.Data);
        switch(info.type){
            case "toy-list":
                // do something
                break;
            case "toy-status":
                // do something
                break;
            case "button-down"
                // do something
                break;
            case "....":
                // and so on...
                break;
            default:
                Logger.Log("Events {0}:{1}",info.type,info.data);
        }
    }
}
const ws = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxxx/v1")
ws.addEventListener("message", function(event) {
  const { type, data } = JSON.parse(event.data)
  switch (type) {
    case "toy-list":
      // do something
      break
    case "toy-status":
      // do something
      break
    case "button-down":
      // do something
      break
    // ...
    default:
      console.log(data)
  }
}

4. Request access

Send an access message to websocket server

{
  "type": "access",
  "data": {
    "appName": "Your App Name" // will show in Lovense Remote screen
  }
}

Example:

// Request access
private void Socket_Connection(IWebSocket socket)
{
    socket = new WebSocket(address);
    socket.OnOpen += Socket_onOpen;
    socket.OnMessage += Socket_onMessage;
    socket.OnClose += Socket_OnClose;
    socket.OnError += Socket_OnError;
    socket.ConnectAsync();
}
private void Socket_OnMessage(object sender, MesageEventArgs e)
{
    if(e.IsBinary){
        Logger.Log("Receive Bytes({1}): {0}", e.Data, e.RawData.Length);
    }else if(e.IsText){
        Logger.Log("Receive Text: {0}", e.Data);
    }
}

string message = "{type:\"access\",data:{ appName: \"Your App Name\"}}";

socket.SendAsync(message);
const ws = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxxx/ws")
ws.addEventListener("message", function(event) {
  console.log(event.data)
}

const message = {
  type: "access",
  data: {
    appName: "Your App Name"
  }
}

ws.send(JSON.stringify(message))

5. Confirm connection

You will receive an access-granted type message from the websocket server.

Example:

// confirm connection
[System.Serializable]
public class PlayerInfo{
  private string type;
  private T data;
  ...
}

public string address = "ws://xxx.xxx.xxx.xxx:xxxxx/v1";
private IWebSocket socket;
private void Socket_Connection(IWebSocket socket)
{
socket = new WebSocket(address);
socket.OnOpen += Socket_onOpen;
socket.OnMessage += Socket_onMessage;
socket.OnClose += Socket_OnClose;
socket.OnError += Socket_OnError;
socket.ConnectAsync();
}
private void Socket_OnMessage(object sender, MesageEventArgs e)
{
if(e.IsBinary){
Logger.Log("Receive Bytes({1}): {0}", e.Data, e.RawData.Length);
}else if(e.IsText){  
Logger.Log("Receive Text: {0}", e.Data);
PlayerInfo info = JsonUtility.FromJson<PlayerInfo>(e.Data);
switch(info.type){
case "access-granted":
// do something
break;
default:
Logger.Log("Events {0}:{1}",info.type,info.data);
}
}
}

const ws = new WebSocket("ws://xxx.xxx.xxx.xxx:xxxxx/v1")
ws.addEventListener("message", function(event) {
const { type, data } = JSON.parse(event.data)
switch (type) {
  case "access-granted":
    // now you will receive toy events
    break
  default:
    console.log(data)
}

6. Maintain the connection

Send a ping message to the websocket server every 5 seconds to keep the connection alive. Otherwise, the websocket server will close the connection.

You will receive a pong message from the websocket server when you send a ping message. That can be used to check if the websocket server is still active.

// Send keep-alive message
private void Socket_OnMessage(object sender, MesageEventArgs e)
{
    if(e.IsBinary){
        Logger.Log("Receive Bytes({1}): {0}", e.Data, e.RawData.Length);
    }else if(e.IsText){
        Send_KeepAlived(e.Data);
        Logger.Log("Receive Text: {0}", e.Data);
    }
}
private void Send_KeepAlived(string msg){
    if(msg !=null){
        JsonData jsonTemp = new JsonData();
        jsonTemp = JsonUtility.FromJson<JsonData>(msg);
        Logger.Log("Receive Ping:{0}",jsonTemp)
            if(jsonTemp.type == "ping"){
                socket.SendAsync("Pong");
            }
    }
}
ws.send(
  JSON.stringify({
    type: "ping",
  })
)

ws.addEventListener("message", function (event) {
  const data = JSON.parse(event.data)
  if (data.type === "pong") {
    console.log("pong")
  }
})

Events

Toy List

This event will be sent when the user disables/enables a toy, adds a new toy, or removes a toy.

Toy firmware version must be > 500

{
  "type": "toy-list",
  "toyList": [
    {
      "id": "toy-id",
      "name": "Max 2",
      "type": "max",
      "hVersion": "2",
      "fVersion": 300,
      "nickname": "toy-nickname",
      "battery": 100,
      "connected": true
    }
    // ...
  ]
}

Toy Status

When the toy is disconnected (because of a disconnected signal, low battery, or manual power off) or when the toy is reconnected to the Lovense app, this event is sent.

{
  "type": "toy-status",
  "toyId": "toy-id",
  "data": {
    "connected": true // true: connected, false: disconnected
  }
}

Button Down

This event is sent when the user presses a button on a Lovense toy.

{
  "type": "button-down",
  "toyId": "toy-id",
  "data": {
    "index": 0
  }
}

Button Up

This event is sent when the user releases a button on a Lovense toy.

{
  "type": "button-up",
  "toyId": "toy-id",
  "data": {
    "index": 0
  }
}

Button Pressed

When the toy's button is pressed, a pressed event is sent.

{
  "type": "button-pressed",
  "toyId": "toy-id",
  "data": {
    "index": 0
  }
}

Function Strength Changed

If there are any toy strength changes, a function-strength-changed event is sent.

Vibration example:

{
  "type": "function-strength-changed",
  "toyId": "toy-id",
  "data": {
    "function": "vibration",
    "value": 20, // 0 ~ 20
    "index": 0 // 0 ~ 3
  }
}

Rotation example:

{
  "type": "function-strength-changed",
  "toyId": "toy-id",
  "data": {
    "function": "rotation",
    "value": 20, // 0 ~ 20
    "index": 0 // 0 ~ 3
  }
}

Inflation example:

{
  "type": "function-strength-changed",
  "toyId": "toy-id",
  "data": {
    "function": "inflation",
    "value": 3, // 0 ~ 3
    "index": 0 // 0 ~ 3
  }
}

Thrusting example:

{
  "type": "function-strength-changed",
  "toyId": "toy-id",
  "data": {
    "function": "thrusting",
    "value": 20, // 0 ~ 20
    "index": 0 // 0 ~ 3
  }
}

Shake

Every time the user shakes the toy, a shake event sent once.

{
  "type": "shake",
  "toyId": "toy-id"
}

Shake Frequency Changed

When the frequency with which the user shakes the toy changes, a shake-frequency-changed event is sent.

{
  "type": "shake-frequency-changed",
  "toyId": "toy-id",
  "data": {
    "value": 0.5 // 0 ~ 1
  }
}

Depth Changed

When the user's inserted depth on the toy changes, a depth-changed event is sent.

{
  "type": "depth-changed",
  "toyId": "toy-id",
  "data": {
    "value": 0 // 0 ~ 20
  }
}

Battery Changed

When the toy's battery level changes, a battery-changed event is sent.

{
  "type": "battery-changed",
  "toyId": "toy-id",
  "data": {
    "value": 100 // 0 ~ 100
  }
}

Motion Changed

When the toy's motion changes, a motion-changed event is triggered. Motion data is collected every 20ms, and then 5 data are grouped into one event. As a result, the event data is sent approximately every 100ms.

Here is an illustration depicting the direction of the toy:

Motion changed direction introduction

{
  "type": "motion-changed",
  "toyId": "toy-id",
  "data": {
    "motionData": [
      {
        "direction": 1, // 0:up/inward, 1:down/outward
        "speed": 20, // 0 ~ 100
        "position": 85 // 0 ~ 100
      },
      {
        "direction": 1,
        "speed": 20,
        "position": 75
      },
      {
        "direction": 1,
        "speed": 20,
        "position": 75
      },
      {
        "direction": 0,
        "speed": 20,
        "position": 80
      },
      {
        "direction": 1,
        "speed": 20,
        "position": 75
      }
    ]
  }
}

Event Closed

When the user disables game mode, a event-closed event is sent. Then the websocket connection will be closed.

{
  "type": "event-closed"
}

Supported Toy Events

ToyButtonFunction Strength ChangedShakeShake Frequency ChangedBattery ChangedDepth ChangedMotion Changed
Nora✅ index:0,1✅ function: vibration,rotation✅✅✅
Max 2✅ index:0,1✅ function: vibration,inflation✅✅✅
Solace✅ index:0,1,2,3,✅✅
Solace Pro✅ index:0,1,2,3,✅✅
Mission2✅ index:0,1✅✅
Last Updated:
Explore our Forum or Support to get more inspiration or solve your problems.
Discord Channel
It is an online real-time channel where you can communicate directly with our official administrators or many excellent developers.
Forum
It is a place for Lovense developers to communicate, where you can find solutions to problems or get inspired for your projects.
Support
Find documents and tutorials that may be helpful to you.