Cam Solutions

Cam Extension for Chrome

The Cam Extension for Chrome is the best solution for cam models to stream with Lovense toys, featuring:

  • Highly customizable vibration levels and patterns
  • Lovense widget for quick access to settings while streaming
  • Video overlay support for OBS, Streamster, SplitCam
  • Games and tools to increase interactivity and earn more tips
  • Frequent updates, improvements, and new features

Cam Extension Setting Page

Step 1: Configure the developer dashboard

FieldDescription
Website NameThe name that will be displayed in our cam site list (in the Cam Extension). Contact us if you want to change it.
Website URLYour home page URL.
Model Broadcasting URL PatternThe URL pattern of your model broadcasting page.
StatusThe status of your website.

Go to the developer dashboardopen in new window and fill in your settings.

Status Meaning:

Pending - The website is being tested. Use test:{Website Name} name in order to add your website to the Cam Extension during the testing phase.

Active - The website is live and public. All models can see your cam site in the Cam Extension.

Step 2: Integration

Import the Javascript file

Import the Javascript file to your model’s broadcasting page. This Javascript will declare a global variable CamExtension on the page.

<script src="https://api.lovense-api.com/cam-extension/static/js-sdk/broadcast.js"></script>
1

Initialize the instance object

Initialize a CamExtension instance on the model’s live page.

/**
 * @param {string} websiteName this is the Website Name shown in the developer dashboard
 * @param {string} modelName this is the model's name
 * @returns {object} CamExtension instance object
 */
const camExtension = new CamExtension(websiteName, modelName)
1
2
3
4
5
6

Listen for ready event

Listen for the ready event, which will be called after successful initialization. You can communicate with the Cam Extension after this event is triggered.

/**
 * readyCallback
 * @param {object} ce CamExtension instance object
 */
const readyCallback = function (ce) {
  // Handle the CamExtension instance object
  // e.g. await ce.getCamVersion()
}

camExtension.on("ready", readyCallback)
1
2
3
4
5
6
7
8
9
10

Step 3: Methods & events

Methods

  • receiveTip

    Call this method when the model receives a tip. The Cam Extension will trigger a response in the toy according to these parameters:

    /**
     * receiveTip
     * @param {number} amount  tip amount that the model receives
     * @param {string} tipperName this is the tipper’s Screen Name
     */
    camExtension.receiveTip(amount, tipperName)
    
    1
    2
    3
    4
    5
    6
  • receiveMessage

    Call this method when the model receives a message in the chat room.

    /**
     * receiveMessage
     * @param {string} userName  the sender’s Screen Name
     * @param {string} content the message just sent by the sender
     */
    camExtension.receiveMessage(userName, content)
    
    1
    2
    3
    4
    5
    6
  • getSettings

    Get the model's Lovense Settings.

    /**
     * getSettings
     * @returns {object} model's Lovense Settings
     */
    const data = await camExtension.getSettings()
    // data = {
    //  levels: {
    //    level1: {
    //     min: 1,
    //     max: 9,
    //     time: 2,
    //     rLevel: 0,
    //     vLevel: 0,
    //    },
    //    level2: {...}
    //    level3: {...}
    //  },
    //  special: {
    //    earthquake: {
    //      enable: true,
    //      type: "earthquake",
    //      time: "22",
    //      token: "120",
    //    },
    //    fireworks: (...),
    //    wave: (...),
    //    pulse: (...),
    //    random: (...),
    //    randomTime: (...),
    //    giveControl: (...),
    //    pause: (...),
    //    clear: (...),
    //  }
    // }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
  • getToyStatus

    Get the model's Lovense Toys status.

    /**
     * getToyStatus
     * @returns {array} model's Lovense Toys status.
     */
    const data = await camExtension.getToyStatus()
    // data = [
    //  {
    //    id: "l58f167da065",
    //    name: "",
    //    type: "lush",
    //    status: "on"
    //  },
    //  {...}
    // ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • getCamVersion

    Get the Cam Extension version.

    /**
     * getCamVersion
     * @returns {string} Cam Extension version
     */
    const data = await camExtension.getCamVersion()
    // data = "30.4.4"
    
    1
    2
    3
    4
    5
    6

Events

You can listen for certain events to get the latest real-time data

  • postMessage

    We will trigger this event when we need to send a message to you or the chat room, and you will need to help us handle it accordingly.

    camExtension.on("postMessage", (message) => {
      // Process the message to be sent
      // Send the message to chat room
      // e.g. message = "My LOVENSE Lush is now reacting to john's tip. It will stop after 5 sec!"
    })
    
    1
    2
    3
    4
    5
  • toyStatusChange

    We will trigger this event when the toy status changes.

    camExtension.on("toyStatusChange", (data) => {
      // Handle toy information data
      // data = [{
      //  id: "d6c35fe83348",
      //  name: "toy's name",
      //  type: "lush",
      //  status: "on",
      //  version: "",
      //  battery: "80"
      // }]
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • tipQueueChange

    We will trigger this event when the tip queue updates.

    camExtension.on("tipQueueChange", (data) => {
      //  handle queue information data
      //  data = {
      //   running: [
      //    {
      //      amount: 100,
      //      tipperName: "john",
      //      time: 20,
      //      module: "Special Command",
      //      cParameter: {},
      //      level: "",
      //      specialType: "earthquake",
      //      modelName: "coco",
      //      reactToys: [,
      //        toyId: "d6c35fe83348",
      //        specialType: "earthquake",
      //        status: 1,
      //        toyType: "lush",
      //      ]
      //    }
      //   ],
      //   queue: [...],
      //   waiting: [...]
      //  }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  • settingsChange

    We will trigger this event when the model’s Lovense Settings are updated.

    camExtension.on("settingsChange", (data) => {
      //  handle configuration information data
      // data = {
      //  levels: {
      //    level1: {
      //     min: 1,
      //     max: 9,
      //     time: 2,
      //     rLevel: 0,
      //     vLevel: 0,
      //    },
      //    level2: {...}
      //    level3: {...}
      //  },
      //  special: {
      //    earthquake: {
      //      enable: true,
      //      time: "20",
      //      token: "100",
      //    },
      //    fireworks: (
      //      enable: true,
      //      time: "22",
      //      token: "120",
      //    ),
      //    giveControl: (
      //      enable: false,
      //      time: "",
      //      tokensBegin: "",
      //      tokensEnd: "",
      //    ),
      //    randomTime: (
      //      enable: true,
      //      tokens: "38",
      //      minTime: 10,
      //      maxTime: 50,
      //      level: 5,
      //    ),
      //    pause: (...),
      //    pulse: (...),
      //    random: (...),
      //    wave: (...),
      //  }
      // }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45

Cam Kit for Web

Work Flow

Step 1: Configure the developer dashboard

Go to the developer dashboardopen in new window

  1. Get your dToken (Developer Token).
  2. Click "Cam Kit Developer settings (v2.0)", configure your settings.

Step 2: Generate an mToken for your model

Get a unique token for your model. You only need to do this once for each model.

API URL: https://api.lovense-api.com/api/cam/model/getToken

Notice: For security reasons, the developer token should be always used on the server side. You should never use it in your JS code from the client side.

HTTP Request: POST

Content Type: application/x-www-form-urlencoded; charset=UTF-8

Parameters:

NameDescription
dTokenDeveloper Token
mInfoModel's Information (UTF-8 encoded JSON), Format: {"mId": "xxx", "mName": "xxx"}

Sample request:

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "dToken=xxx&mInfo={\"mId\":\"xxx\",\"mName\":\"xxx\"}");
Request request = new Request.Builder()
  .url("https://api.lovense-api.com/api/cam/model/getToken")
  .method("POST", body)
  .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36")
  .addHeader("Content-Type", "application/x-www-form-urlencoded")
  .build();
Response response = client.newCall(request).execute();
1
2
3
4
5
6
7
8
9
10
11
var axios = require('axios');
var qs = require('qs');
var data = qs.stringify({
  'dToken': 'xxx',
  'mInfo': '{"mId":"xxx","mName":"xxx"}' 
});
var config = {
  method: 'post',
  url: 'https://api.lovense-api.com/api/cam/model/getToken',
  headers: { 
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36', 
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Return:

{
  "result": true, // true or false
  "message": "SuccessFully",
  "code": 200, // 200: OK 401: Invalid token 402: Invalid user information 505: Other Errors
  "data": {
    "mId": "xxx",
    "mToken": "95116bd2af44b753da2a1e4cd55cf965" // got mToken
  }
}
1
2
3
4
5
6
7
8
9
NameDescription
resulttrue if successful, false if failed
messageRequest result
dataDetailed description
code200: OK, 401: Invalid token, 402: Invalid user information, 505: Other Errors

Step 3: Model configures their Lovense settings

Display the Lovense settings page on the model's side. You can put this page in a new window or an iframe.

After you modify your settings, the new settings will take around 10 seconds to take effect.

Settings page: https://api.lovense-api.com/api/cam/model/v2/setting?mToken={mToken}

Sample code:

lovense.settingPage = window.open(
  "https://api.lovense-api.com/api/cam/model/v2/setting?mToken={mToken}"
)
1
2
3

or

<iframe
  id="lovense-setting"
  width="100%"
  height="1500px"
  scrolling="no"
  src="https://api.lovense-api.com/api/cam/model/v2/setting?mToken={mToken}"
></iframe>
1
2
3
4
5
6
7

Step 4: Broadcasting process & methods

Include the model.js on your broadcasting page.

<script src="https://api.lovense-api.com/api/cam/model/v2/model.js?mToken={mToken}"></script>
1

Call lovense.addMessageListener(callback) after the live page is loaded, and handle these types of messages in the callback function:

Events

  • message

    lovense.addMessageListener(function (data) {
      // data = {
      //  type: "message",
      //  detail: "some customized messages to be sent to the public chat room."
      //  e.g. detail = "My LOVENSE Lush is now reacting to john's tip. It will stop after 5 sec!"
      //}
    })
    
    1
    2
    3
    4
    5
    6
    7
  • settings

    lovense.addMessageListener(function (data) {
      // Handle settings information
      // data = {
      //  type: "settings",
      //  detail: {
      //   levels: {
      //     level1: {
      //      min: "1",
      //      max: "9",
      //      time: "2",
      //      rLevel: 0,
      //      vLevel: 0,
      //     },
      //     level2: {...}
      //     level3: {...}
      //   },
      //   special: {
      //     earthquake: {
      //       time: "22",
      //       token: "120",
      //     },
      //     fireworks: (...),
      //     giveControl: (...),
      //     pause: (...),
      //     pulse: (...),
      //     random: (...),
      //     twowaves: (...),
      //     wave: (...),
      //   }
      //  }
      // }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  • toy

    lovense.addMessageListener(function (data) {
      // Handle toy information data
      // data = {
      //  type: "toy",
      //  status: "on"
      //  detail: [
      //   {
      //     id: "l58f167da065",
      //     name: "",
      //     type: "lush",
      //     status: "on"
      //   }
      //  ]
      // }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • tipQueueStatus

    lovense.addMessageListener(function (data) {
      // handle queue information data
      // data = {
      //  type: "tipQueueStatus",
      //  detail: {
      //   running: [
      //    {
      //      amount: 20,
      //      tipperName: "john",
      //      time: 30,
      //      cParameter: {},
      //      level: 5,
      //      module: "Basic Level"
      //      specialType: undefined,
      //      name: "coco",
      //      reactToys: [,
      //        toyId: "d6c35fe83348",
      //        specialType: "earthquake",
      //        status: 1,
      //        toyType: "lush",
      //        vibrate: 20,
      //        rotate: 0,
      //        airpump: 0,
      //      ]
      //    }
      //   ],
      //   queue: [
      //    {
      //      amount: 20,
      //      tipperName: "john",
      //    }
      //   ],
      //   waiting: [...]
      //  }
      // }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
  • tipRunning

    lovense.addMessageListener(function (data) {
      // handle running tip information
      // data = {
      //  type: "tipRunning",
      //  detail: {
      //   running: [
      //    {
      //      amount: 20,
      //      tipperName: "john",
      //      time: 30,
      //      cParameter: {},
      //      level: 5,
      //      module: "Basic Level"
      //      specialType: undefined,
      //      name: "coco",
      //      reactToys: [,
      //        toyId: "d6c35fe83348",
      //        specialType: "earthquake",
      //        status: 1,
      //        toyType: "lush",
      //        vibrate: 20,
      //        rotate: 0,
      //        airpump: 0,
      //      ]
      //    }
      //   ]
      //  }
      // }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

Methods

  • initCamApi

    If your website is a single page application, when the model enters the broadcasting page call initCamApi.

    /**
     * initCamApi
     * @param {string} mToken  model token
     */
    lovense.initCamApi(mToken)
    
    1
    2
    3
    4
    5
  • destroyCamApi

    When the model ends the live broadcast or signs out, call destroyCamApi.

    lovense.destroyCamApi()
    
    1
  • receiveTip

    Once a tip is received, call Lovense.receiveTip(cname,amount). Toys will respond according to the settings.

    /**
     * receiveTip
     * @param {string} cname  this is the tipper’s Screen Name
     * @param {number} amount  token amount
     */
    lovense.receiveTip(cname, amount)
    // e.g. lovense.receiveTip('cname', 1)
    
    1
    2
    3
    4
    5
    6
    7
  • getToys

    Use getToys() to get the toy info of models

    /**
     * getToys
     * @returns {array} toy information
     */
    lovense.getToys()
    
    1
    2
    3
    4
    5

    Return:

    [
      {
        "id": "XXXXXXXXXXXX",
        "name": "",
        "status": "on",
        "type": "lush"
      }
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
  • getSettings

    Use getSettings to get the settings info of models

    /**
     * getSettings
     * @returns {object} settings information
     */
    lovense.getSettings()
    
    1
    2
    3
    4
    5

    Return:

    {
        levels: {
          level1: {
            max: 10, // tip level range maximum
            min: 3,  // tip level range minimum
            rLevel: 0, // rotation level
            time: 3, // duration (seconds)
            vLevel: 5  // vibration level
          },
          ...
        },
        special: {
          clear: {
            token: 20 // number of tokens
          },
          earthquake: (...),
          fireworks: (...),
          giveControl: (...),
          pause: (...),
          pulse: (...),
          random: (...),
          twowaves: (...),
          wave: (...)
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

Display Panel (optional)

The Lovense Display Panel makes the show much more interactive compared to simple text based tip activation. We currently provide 2 panels for your tippers/viewers.

  • Control Panel - This enables a new tip menu item called Give Control. It automatically generates a control link for the tipper when they tip for this item, allowing models to give direct control of their toys to tippers much more easily.

Control Panel

  • Tip Panel - Show the model's tip menu directly on the viewer's page.

Control Panel

⚠️This feature can only be used with Cam Extension or Cam Kit

Step 1: Turn on the Display Panel on your Lovense Developer Dashboard

Go to the developer dashboardopen in new window and turn on the Display Panel.

Control PanelControl PanelControl Panel

You will get a default AES KEY and AES IV that are used for encryption. Use them to encrypt the model name and the tipper name.

Control Panel

Step 2: Import the tipper.js to your tipper's page

<script src="https://api.lovense-api.com/api/cam/tipper/v2/tipper.js"></script>
1

Step 3: Initialize

Lovense.init(platform, modelKey, tipperKey)
1

Parameters

NameDescriptionRequired
platformYour Website Name (shown in the Developer Dashboard)yes
modelKeyThe model name encrypted on your server side using the AES KEY and AES IV. Do not expose your Key/IV in your JS code. (There is a demo on how to encrypt text using AES in JAVA below)yes
tipperKeyThe tipper name encrypted using the AES KEY and AES IV (The tipper name should be the Display Name showing in the public broadcasting room)yes

⚠️ Display Panels are only available when the model is using Cam Extension version 30.0.8+

If your website is a single page application, when the tipper leaves the model's room, call:

Lovense.destroy()
1

Here is the demo on how to encrypt text using AES in JAVA:

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;

public class AESDemo {

    private static Key getKey(String key)  throws Exception{
        byte[] keyBytes = key.getBytes("UTF-8");
        SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
        return newKey;
    }
    private static AlgorithmParameters getIV(String iv) throws Exception {
        byte[] ivs = iv.getBytes("UTF-8");
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(ivs));
        return params;
    }

    public static String encrypt(String key,String iv,String text) throws Exception {

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, getKey(key), getIV(iv));
        byte[] encryptedBytes = cipher.doFinal(text.getBytes());
        return new String(Base64.encodeBase64(encryptedBytes, false,true),"UTF-8");
    }

    public static String decrypt(String key,String iv,String text) throws Exception {
        byte[] textBytes = Base64.decodeBase64(text);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, getKey(key), getIV(iv));
        byte[] decodedBytes = cipher.doFinal(textBytes);
        return new String(decodedBytes, "UTF-8");
    }

    public static void main(String[] args) throws Exception{
        String key="jHZZwiizsAF2qTAY";  //use your own Key here
        String iv="zijknVpNWeeTYGPV";  //use your own IV here
        String test="Hello World!";
        System.out.println(encrypt(key,iv,test));;
        System.out.println(decrypt(key,iv,encrypt(key,iv,test)));;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

Step 4: Data Forwarding

Lovense will notify you when the Model Status has changed, and you should send the Model Status back to the tipper.js. Here is the Flow Chart:

Display Panel

How does it work?

Contact us to provide your callback URL. Lovense will post ModelStatus to your callback URL when necessary (for example, when the model enables/disables features during their broadcast). You forward the ModelStatus to the tipper/viewer's page and reply to the callback request with "OK". Then call Lovense.recieveData(data) on the tipper/viewer's page.

Description of the Callback Request

URL: your callback url

Request Protocol: HTTPS Request

Method: POST

Content Type: application/json

the ModelStatus Object::

{
  "from":"ABCDxxxxxxxxxxxx", //Encrypted String using your AES KEY and IV
  "to": {
    "type":"customersOfModel", //which group of people you should forward to
    "target":"Lucy,Tony" // users you should forward the data to
  },
  "data": data // the data you should forward
}
1
2
3
4
5
6
7
8

Attributes in ModelStatus

  • from [String]: Encrypted string of "Lovense" using your AES KEY. If you can decrypt it with your AES KEY, you can confirm that the data is from Lovense. It is a static string until you changed your AES KEY in Lovense Dashboard.
  • to [Object]: Whom you should forward the data to
  • to.type [String]: Which group of people you should forward the data to. Possible values: customersOfModel, customers
  • to.target [String]: Users you should forward to
    • If the to.type is customersOfModel, the to.target is the model's name (separated with commas if there are multiple values), and you should forward the data to all the customers in the specified model's broadcasting room.
    • If the to.type is customer, the to.target is the tipper/viewer's screen name (separated with commas if there are multiple values), and you should forward the data to the specified tipper/viewer
  • data [String]: the data that you should forward

Here is a sample written in JAVA for handling the callback from Lovense:

private final String KEY = "Your KEY";
private final String IV = "Your IV";
private final String AESFrom = AESUtil.encrypt("Lovense",KEY,IV);

@RequestMapping(value = "/lovense/callback")
public @ResponseBody String callback(@RequestBody Map<String,Object> modelStatus) {

  //1. If from is same as AESFrom
  if(AESFrom.equals(modelStatus.get("from"))){
    Map<String,String> to = (Map<String, String>) modelStatus.get("to");
    String data = (String) modelStatus.get("data");

    //2. Handle different target groups
    switch (to.get("type")){
      case "customersOfModel":
      String modelName = to.get("target");
      //3.1 TODO forward `data` to the tippers/viewers in the model's room
      break;
    case "customer":
      String customerScreenName = to.get("target");
      //TODO forward `data` to the tippers/viewers
      break;
    default:
      break;
    }
  }
  //4. Reply "OK"
  return "OK";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Custom Cam Solution

We offer two methods of data forwarding.

If you're developing a web application, we recommend using our Basic JS SDK. It is a JavaScript implementation of the Basic Socket API.

Basic API

This API allows developers to access Lovense toys through simple HTTPS request. Configure your settings on our developer dashboard before getting started.

Here is a sample demo for your reference.

Step 1: Configure the developer dashboard

Go to the developer dashboardopen in new window, select "Standard API" tab, enter your application info.

  • Website Name: The name that will be displayed in Lovense Connect. Contact us if you want to change it.

  • Callback URL: This is the URL from your server where our app sends callback information.

  • Heartbeat: The callback interval for our app to update your server with the latest status.

Step 2: Connect to Lovense toy(s)

We offer 2 ways to connect to our toys.

By Lovense Connect APP (iOS or Android)

Users need to install Lovense Connect mobile app on their iOS/Android devices.

  1. The user pairs their Lovense toy to the Lovense Connect app.

  2. Get the QR code to connect to Lovense Connect.

    Call Lovense Server API from your server

    API URL: https://api.lovense-api.com/api/lan/getQrCode

    Notice: For security reasons, the developer token should be always used on the server side. You should never use it in your JS code from the client side.

    Request Protocol: HTTPS Request

    Method: POST

    Content Type: application/json

    Parameters:

    NameDescriptionRequired
    tokenDeveloper Tokenyes
    uidUser ID on your applicationyes
    unameUser ID on your applicationno
    utokenUser token from your website. This is for your own verification purposes. We suggest you generate a unique token for each user. This allows you to verify the user and avoid others faking the calls.no
    vversionyes

    Request Example:

    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "42425232242",
      "v": 2
    }
    
    1
    2
    3
    4
    5

    Return:

    {
      code: 0
      result: true
      message: "Success"
      data: {
          "qr": "https://test2.lovense.com/UploadFiles/qr/20220106/xxxx.jpg", // qrcode picture
          "code": "xxxx"
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    The API will return a JSON object that contains the QR code image URL. You will get:

    NameDescription
    codeError code
    messageresult
    resulttrue if successful, false if failed
    dataQR code image/code

    Display the QR code to your user.

  3. Users scan the QR code using Lovense Connect.

Once the user scans the QR code with the Lovense Connect app, the app will invoke the callback URL you provided on the developer dashboard.

The Lovense Connect app will send the following POST to your server:

{
  uid: User ID
  utoken: xxx, // User token on your application
  domain: 192-168-0-11.lovense.club, // the domain name of the Lovense Connect app
  httpPort: 34567, // HTTP server port
  wsPort: 34567, // HTTP server port
  httpsPort: 34568, //HTTPS server port
  wssPort: 34568, // HTTPS server port
  platform: 'ios', // the Lovense Connect platform e.g. Android/iOS
  appVersion: '2.3.6', //the Lovense Connect version
  version: 101, // the protocol version e.g. 101
  toys:{
      toyId:{
        id: xxxxxx, // Toy's ID
        name:"lush", // toy's name
        nickName: "nickName"
        status: 1 // e.g. 1/0
      }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

The domain and httpPort used to connect to Lovense Connect (iOS & Android) are returned.

By Lovense Connect PC (Window or Mac)

Windows computers - Users need the Lovense USB Bluetooth Adapter.

Mac - Users can connect our toys directly to their Mac via Bluetooth.

The user pairs their Lovense toy to Lovense Connect for PC/Mac.

Step 3: Command the toy(s)

Once everything is set up, you can command our toys:

  • Local APIs: These APIs are exposed by Lovense Connect app through HTTPS. The connections are in the same LAN or the same computer.
  • Server APIs: If your application can’t establish a LAN connection to the user’s Lovense Connect, you can use the Server API to connect the user’s toy.
By local application

⚠️ Requires Lovense Connect for iOS v2.6.4 or later, Android v2.7.7 or later, or PC v.1.5.4 or later.

If the user's device is in the same LAN environment, a POST request to Lovense Connect can be used to trigger a toy response. In this case, both your server and Lovense's server are not required.

If the user has Lovense Connect mobile, the domain and httpsPort are accessed from the callback information. If the user has Lovense Connect for PC/Mac, the domain is 127-0-0-1.lovense.club, and the httpsPort is 30010.

With the same command line, different parameters will lead to different results as below.

  • GetToys Request

    Get the user's toy(s) information.

    API URL: https://{domain}:{httpsPort}/command

    Request Protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    commandType of requestString/yes

    Request Example:

    {
      "command": "GetToys"
    }
    
    1
    2
    3

    Response Example:

    {
      "code": 200,
      "data": {
        "toys": "{  \"fc9f37e96593\" : {    \"id\" : \"fc9f37e96593\",    \"status\" : \"1\",    \"version\" : \"\",    \"name\" : \"nora\",    \"battery\" : 100,    \"nickName\" : \"\"  }}",
        "platform": "ios",
        "appType": "connect"
      },
      "type": "OK"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • GetToyName Request

    Get the user's toy(s) name.

    API URL: https://{domain}:{httpsPort}/command

    Request Protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    commandType of requestString/yes

    Request Example:

    {
      "command": "GetToyName"
    }
    
    1
    2
    3

    Response Example:

    {
      "code": 200,
      "data": [
        "Domi",
        "Nora"
      ],
      "type": "OK"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Function Request

    API URL: https://{domain}:{httpsPort}/command

    Request Protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    commandType of requestString/yes
    actionControl the function and strength of the toystringActions can be Vibrate, Rotate, Pump, Thrusting, Fingering, Suction or Stop. Use All to make all functions respond. Use Stop to stop the toy’s response.
    Range:
    Vibrate:0 ~ 20
    Rotate: 0~20
    Pump:0~3
    Thrusting:0~20
    Fingering:0~20
    Suction:0~20
    All:0~20
    yes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    loopRunningSecRunning timedoubleShould be greater than 1.no
    loopPauseSecSuspend timedoubleShould be greater than 1.no
    toyToy IDstringIf you don’t include this, it will be applied to all toysno
    stopPreviousStop all previous commands and execute current commandsintDefault: 1, If set to 0 , it will not stop the previous command.no
    apiVerThe version of the requestintAlways use 1yes

    The stopPrevious parameter is available in the following versions: Android Connect 2.7.6, iOS Connect 2.7.2, PC Connect 1.5.9.

    Request Example:

    // Vibrate toy ff922f7fd345 at 16th strength, run 9 seconds then suspend 4 seconds. It will be looped. Total running time is 20 seconds.
    {
      "command": "Function",
      "action": "Vibrate:16",
      "timeSec": 20,
      "loopRunningSec": 9,
      "loopPauseSec": 4,
      "toy": "ff922f7fd345",
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Vibrate 9 seconds at 2nd strength
    // Rotate toys 9 seconds at 3rd strength
    // Pump all toys 9 seconds at 4th strength
    // For all toys, it will run 9 seconds then suspend 4 seconds. It will be looped. Total running time is 20 seconds.
    {
      "command": "Function",
      "action": "Vibrate:2,Rotate:3,Pump:3",
      "timeSec": 20,
      "loopRunningSec": 9,
      "loopPauseSec": 4,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Vibrate 9 seconds at 2nd strength
    // The rest of the functions respond to 10th strength 9 seconds
    {
      "command": "Function",
      "action": "Vibrate:2,All:10",
      "timeSec": 20,
      "loopRunningSec": 9,
      "loopPauseSec": 4,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Stop all toys
    {
      "command": "Function",
      "action": "Stop",
      "timeSec": 0,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
  • Pattern Request

    If you want to change the way the toy responds very frequently you can use a pattern request. To avoid network pressure and obtain a stable response, use the commands below to send your predefined patterns at once.

    API URL: https://{domain}:{httpsPort}/command

    Request protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    commandType of requestString/yes
    rule"V:1;F:v,r,p,t,f,s;S:1000#"
    V:1; Protocol version, this is static;
    F:v,r,p,t,f,s; Features: v is vibrate, r is rotate, p is pump, t is thrusting, f is fingering, s is suction, this should match the strength below. F:; Leave blank to make all functions respond;
    S:1000; Intervals in Milliseconds, should be greater than 100.
    stringThe strength of r and p will automatically correspond to v.yes
    strengthThe pattern
    For example: 20;20;5;20;10
    stringNo more than 50 parameters. Use semicolon ; to separate each strength.yes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    toyToy IDstringIf you don’t include this, it will apply to all toysno
    apiVerThe version of the requestintAlways use 2yes

    Request Example:

    // Vibrate the toy as defined. The interval between changes is 1 second. Total running time is 9 seconds.
    {
      "command": "Pattern",
      "rule": "V:1;F:v;S:1000#",
      "strength": "20;20;5;20;10",
      "timeSec": 9,
      "toy": "ff922f7fd345",
      "apiVer": 2
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // Vibrate the toys as defined. The interval between changes is 0.1 second. Total running time is 9 seconds.
    // If the toys include Nora or Max, they will automatically rotate or pump, you don't need to define it.
    {
      "command": "Pattern",
      "rule": "V:1;F:v,r,p;S:100#",
      "strength": "20;20;5;20;10",
      "timeSec": 9,
      "apiVer": 2
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • Preset Request

    API URL: https://{domain}:{httpsPort}/command

    Request protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    commandType of requestString/yes
    namePreset pattern namestringWe provide four preset patterns in the Lovense Connect app: pulse, wave, fireworks, earthquakeyes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    toyToy IDstringIf you don’t include this, it will be applied to all toysno
    apiVerThe version of the requestintAlways use 1yes

    Request Example:

    // Vibrate the toy with pulse pattern, the running time is 9 seconds.
    {
      "command": "Preset",
      "name": "pulse",
      "timeSec": 9,
      "toy": "ff922f7fd345",
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

Response Example:

{
  "code": 200,
  "type": "ok"
}
1
2
3
4

Error Codes:

CodeMessage
500HTTP server not started or disabled
400Invalid Command
401Toy Not Found
402Toy Not Connected
403Toy Doesn't Support This Command
404Invalid Parameter
506Server Error. Restart Lovense Connect.
By server

If your application can’t establish a LAN connection to the user’s Lovense Connect, you can use the Server API to connect the user’s toy.

⚠️ Coming soon to PC Connect.

  • Function Request

    API URL: https://api.lovense-api.com/api/lan/v2/command

    Request Protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Request Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    tokenYour developer tokenstringyes
    uidYour user’s IDstringTo send commands to multiple users at the same time, add all the user IDs separated by commas. The toy parameter below will be ignored and the commands will go to all user toys by default.yes
    commandType of requestString/yes
    actionControl the function and strength of the toystringActions can be Vibrate, Rotate, Pump, Thrusting, Fingering, Suction, or Stop. Use Stop to stop the toy’s response.
    Range:
    Vibrate:0 ~ 20
    Rotate: 0~20
    Pump:0~3
    Thrusting:0~20
    Fingering:0~20
    Suction:0~20
    yes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    loopRunningSecRunning timedoubleShould be greater than 1no
    loopPauseSecSuspend timedoubleShould be greater than 1no
    toyToy IDstringIf you don’t include this, it will be applied to all toysno
    stopPreviousStop all previous commands and execute current commandsintDefault: 1, If set to 0 , it will not stop the previous command.no
    apiVerThe version of the requestintAlways use 1yes

    The stopPrevious parameter is available in the following versions: Android Connect 2.7.6, iOS Connect 2.7.2, PC Connect 1.5.9.

    Request Example:

    // Vibrate toy ff922f7fd345 at 16th strength, run 9 seconds then suspend 4 seconds. It will be looped. Total running time is 20 seconds.
    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "1132fsdfsd",
      "command": "Function",
      "action": "Vibrate:16",
      "timeSec": 20,
      "loopRunningSec": 9,
      "loopPauseSec": 4,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Vibrate 9 seconds at 2nd strength
    // Rotate toys 9 seconds at 3rd strength
    // Pump all toys 9 seconds at 4th strength
    // For all toys, it will run 9 seconds then suspend 4 seconds. It will be looped. Total running time is 20 seconds.
    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "1132fsdfsd",
      "command": "Function",
      "action": "Vibrate:2,Rotate:3,Pump:3",
      "timeSec": 20,
      "loopRunningSec": 9,
      "loopPauseSec": 4,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Stop all toys
    {
      "command": "Function",
      "action": "Stop",
      "timeSec": 0,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
  • Pattern Request

    If you want to change the way the toy responds very frequently you can use a pattern request. To avoid network pressure and obtain a stable response, use the commands below to send your predefined patterns at once.

    API URL: https://api.lovense-api.com/api/lan/v2/command

    Request protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Response Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    tokenYour developer tokenstringyes
    uidYour user’s IDstringTo send commands to multiple users at the same time, add all the user IDs separated by commas. The toy parameter below will be ignored and the commands will go to all user toys by default.yes
    commandType of requestString/yes
    rule"V:1;F:v,r,p,t,f,s;S:1000#"
    V:1; Protocol version, this is static;
    F:v,r,p,t,f,s; Features: v is vibrate, r is rotate, p is pump, t is thrusting, f is fingering, s is suction, this should match the strength below;
    S:1000; Intervals in Milliseconds, should be greater than 100.
    stringThe strength of r and p will automatically correspond to v.yes
    strengthThe pattern
    For example: 20;20;5;20;10
    stringNo more than 50 parameters. Use semicolon ; to separate every strength.yes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    toyToy IDstringIf you don’t include this, it will apply to all toysno
    apiVerThe version of the requestintAlways use 2yes

    Request Example:

    // Vibrate the toy as defined. The interval between changes is 1 second. Total running time is 9 seconds.
    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "1ads22adsf",
      "command": "Pattern",
      "rule": "V:1;F:v;S:1000#",
      "strength": "20;20;5;20;10",
      "timeSec": 9,
      "apiVer": 2
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Vibrate the toys as defined. The interval between changes is 0.1 second. Total running time is 9 seconds.
    // If the toys include Nora or Max, they will automatically rotate or pump, you don't need to define it.
    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "1ads22adsf",
      "command": "Pattern",
      "rule": "V:1;F:v,r,p;S:100#",
      "strength": "20;20;5;20;10",
      "timeSec": 9,
      "apiVer": 2
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • Preset Request

    API URL: https://api.lovense-api.com/api/lan/v2/command

    Request protocol: HTTPS Request

    Method: POST

    Request Content Type: application/json

    Request Format: JSON

    Parameters:

    NameDescriptionTypeNoteRequired
    tokenYour developer tokenstringyes
    uidYour user’s IDstringTo send commands to multiple users at the same time, add all the user IDs separated by commas. The toy parameter below will be ignored and the commands will go to all user toys by default.yes
    commandType of requestString/yes
    namePreset pattern namestringWe provide four preset patterns in the Lovense Remote app: pulse, wave, fireworks, earthquakeyes
    timeSecTotal running timedouble0 = indefinite length
    Otherwise, running time should be greater than 1.
    yes
    toyToy IDstringIf you don’t include this, it will be applied to all toysno
    apiVerThe version of the requestintAlways use 1yes

    Request Example:

    // Vibrate the toy with pulse pattern, the running time is 9 seconds.
    {
      "token": "FE1TxWpTciAl4E2QfYEfPLvo2jf8V6WJWkLJtzLqv/nB2AMos9XuWzgQNrbXSi6n",
      "uid": "1adsf2323",
      "command": "Preset",
      "name": "pulse",
      "timeSec": 9,
      "apiVer": 1
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

Response Example:

{
  "result": true,
  "code": 200,
  "message": "Success"
}
1
2
3
4
5

Server Error Codes:

CodeMessage
200Success
400Invalid command
404Invalid Parameter
501Invalid token
502You do not have permission to use this API
503Invalid User ID
507Lovense APP is offline

Basic Socket API

Work Flow

Step 1: Application for user authentication token

Use your developer token to apply for an authentication token for your users. You can get the developer token from developer dashboardopen in new window.

WARNING

For security reasons, the developer token should always be used on the server side. You should never use it in your JS code from the client side.

API URL: https://api.lovense-api.com/api/basicApi/getToken

Request Protocol: HTTPS Request

Method: POST

Request Content Type: application/json

Parameters:

NameDescriptionRequired
tokenDeveloper Tokenyes
uidUser ID on your applicationyes
unameUser nickname on your applicationno
utokenUser token from your website. This is for your own verification purposes, and we suggest you generate a unique token for each user. This allows you to verify the user and avoid faking the calls.no

Example:

String url= "https://api.lovense.com/api/basicApi/getToken";
Map<String, String> requestParameter = new HashMap<String, String>();
//TODO initialize your parameters:
requestParameter.put("token", "{Lovense developer token}");
requestParameter.put("uid", "{user ID on your application}");
requestParameter.put("uname", "{user nickname on your application}");
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
if (requestParameter != null && !requestParameter.isEmpty()) {
  Set<String> keys = requestParameter.keySet();
  for (String key : keys) {
    nameValuePairs.add(new BasicNameValuePair(key, requestParameter.get(key)));
  }
}
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import axios from 'axios'

const result = await axios.post(
  "https://api.lovense.com/api/basicApi/getToken",
  {
    token: "{Lovense developer token}",
    uid: "{user ID on your application}",
    uname: "{user nickname on your application}"
  }
)
1
2
3
4
5
6
7
8
9
10

Result:

{
  code: 0
  message: "Success"
  data: {
      "authToken": "{autoToken}"
  }
}
1
2
3
4
5
6
7

Step 2: Validate authorization

After generating the authToken in the previous step, submit it to Lovense on the client-side to verify authorization. This will return socket connection information.

WARNING

Please use Socket.IO for client 2.xopen in new window when connecting to the socket service on the client side

API URL: https://api.lovense-api.com/api/basicApi/getSocketUrl

Method: POST

Content Type: application/json

Parameters:

NameDescriptionRequired
platformYour Website Name (shown in the Developer Dashboard)yes
authTokenAuthorization tokenyes

Return:

NameDescriptionType
codereturn code, 0 for successint
messagereason for failure when the request failsstring
dataresult dataobject
data.socketIoPathsocket.io pathstring
data.socketIoUrlsocket.io urlstring

Example:

String url= "https://api.lovense.com/api/basicApi/getSocketUrl";
Map<String, String> requestParameter = new HashMap<String, String>();
//TODO initialize your parameters:
requestParameter.put("platform", "{platform}");
requestParameter.put("authToken", "{authToken}");
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
if (requestParameter != null && !requestParameter.isEmpty()) {
  Set<String> keys = requestParameter.keySet();
  for (String key : keys) {
    nameValuePairs.add(new BasicNameValuePair(key, requestParameter.get(key)));
  }
}
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import axios from 'axios'

const result = await axios.post(
  "https://api.lovense.com/api/basicApi/getSocketUrl",
  {
    platform: "{platform}",
    authToken: "{authToken}"
  }
)
1
2
3
4
5
6
7
8
9

Result:

{
  code: 0
  message: "Success"
  data: {
      "socketIoPath": "/xxx",
      "socketIoUrl": "xxx"
  }
}
1
2
3
4
5
6
7
8

Step 3: Get QR code

After connecting the socket, obtain the user's QR code information by sending the basicapi_get_qrcode_ts event, which is used for interface display. The response data is returned with the event basicapi_get_qrcode_tc

Emit Event: basicapi_get_qrcode_ts

Listen Event: basicapi_get_qrcode_tc

Return:

NameDescriptionType
codeReturn code, 0 for successint
messageReason for failure when the request failsstring
dataResult dataobject
data.qrcodeQR code raw data. You can use it to generate a QR codestring
data.qrcodeUrlQR code picture urlstring
data.ackIdIf you pass a ackId when sending an event, a consistent ackId will be returned herestring

Example:

import io from 'socket.io-client'

const socket = io('socketIoUrl', { path: 'socketIoPath', transports: ['websocket'] })
const ackId = '24fsf2536fs7324hj647f5'

socket.emit('basicapi_get_qrcode_ts', {
  ackId: ackId
})

socket.on('basicapi_get_qrcode_tc', res => {
  let resData = res ? JSON.parse(res) : {}
  if (resData.data && resData.data.ackId === ackId) {
    console.log(resData)
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Result:

{
  code: 0
  message: "Success"
  data: {
    "qrcodeUrl": "{qrcodeUrl}",
    "qrcode": "{qrcode}"
    "ackId": "24fsf2536fs7324hj647f5"
  }
}
1
2
3
4
5
6
7
8
9

Step 4: Get device information

The toy and device information can be obtained with the event basicapi_update_device_info_tc

Listen Event: basicapi_update_device_info_tc

Example:

import io from 'socket.io-client'

const socket = io('socketIoUrl', { path: 'socketIoPath', transports: ['websocket'] })

socket.on('basicapi_update_device_info_tc', res => {
  let resData = res ? JSON.parse(res) : {}
  console.log(resData)
})
1
2
3
4
5
6
7
8

Result:

{
  "deviceCode": "xxxxxx",
  "online": true,
  "domain": "192.168.1.xx.lovense.club",
  "httpsPort": 30010,
  "wssPort": 30110,
  "appVersion": "1.3.7",
  "platform": "android",
  "appType": "remote",
  "toyList": [
    {
      "id": "xxxxxxxx",
      "name": "Lush 3",
      "toyType": "lush",
      "nickname": "My Lush",
      "hVersion": "3",
      "fVersion": 300,
      "battery": 100,
      "connected": true
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Step 5: Command the toy(s)

We provide two ways to send toy commands.

By local

If the user's app and your application are on the same LAN, you can use the obtained device information to send toy command. Please refer to the documentation for specific parameters.

By server

You can also send commands remotely with the latest basicapi_send_toy_command_ts event. The parameters are the same as those sent by local application.

import io from 'socket.io-client'

const socket = io('socketIoUrl', { path: 'socketIoPath', transports: ['websocket'] })

socket.emit('basicapi_send_toy_command_ts', {
  command: "Function",
  action: "Vibrate:16",
  timeSec: 20,
  apiVer: 1
})
1
2
3
4
5
6
7
8
9
10

Socket Event List

Listen
NameDescriptionTrigger
basicapi_get_qrcode_tcReturns QR code informationwhen 'basicapi_get_qrcode_ts' event is sent
basicapi_update_device_info_tcReturns device informationdevice information update
basicapi_update_app_status_tcReturns the app connection statususer scans the code to establish a connection
basicapi_update_app_online_tcReturns the app network statusthe connection status of Lovense APP and Lovense server
Emit
NameDescription
basicapi_get_qrcode_tsget QR code information
basicapi_send_toy_command_tssend toy commands by server

Basic JS SDK

Basic JS SDK is a javascript implementation of the Basic Socket API solution above. We suggest to directly integrate this if you are a web application developer.

Here is a sample Demo for your reference.

Import the Javascript file

Import the Javascript file to your web page. This Javascript will declare a global variable LovenseBasicSdk on the page.

<script src="https://api.lovense-api.com/basic-sdk/core.min.js"></script>
1

TIP

Please add the following domains to your CSP whitelist.

*.lovense.com *.lovense-api.com *.lovense.club:*

Initialize

Declare an instance object using the LovenseBasicSdk constructor. The ready event is triggered after successful declaration.

Please refer here to see how to request authToken for your users.

/**
 * @param {object} option
 * @param {string} option.uid user ID on your application
 * @param {string} option.platform this is the Website Name shown in the developer dashboard
 * @param {string} option.authToken authorization token
 * @param {boolean} option.debug optional, whether to print debug messages on the console
 * @returns {object} instance object
 */
LovenseBasicSdk(option)
1
2
3
4
5
6
7
8
9

Example:

const basicSdkInstance = new LovenseBasicSdk({
  platform: '{platform}',
  authToken: '{authToken}',
  uid: '{uid}',
  debug: true
})
basicSdkInstance.on('ready', instance => {
  console.log('ready')
})
1
2
3
4
5
6
7
8
9

Methods

getQrcode

Get the QR code to display on the interface. This is an asynchronous method.

Example:

const basicSdkInstance = new LovenseBasicSdk({
  platform: '{platform}',
  authToken: '{authToken}',
  uid: '{uid}',
  debug: true
})
basicSdkInstance.on('ready', async instance => {
  try {
    const codeRes = await instance.getQrcode()
    console.log(codeRes)
    // return:
    // {
    //   "qrcodeUrl": "https://apps.lovense-api.com/UploadFiles/qr/20220725/9b03dfb900af4328b2eb0573a39ec5e0.jpg",
    //   "qrcode": "{\"type\":5,\"data\":\"2Td5iU0YoWSpsE4fx5WSMUbt+khTj0d/GggSrRTVs8Sz4EOOpoISvcRUO3P6/WFxA/FHwfEgLkuCG4kP2m1X2Q==\"}"
    // }
  } catch (e) {
    console.error(e.message)
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
getAppStatus

Returns the app connection status.

Example:

basicSdkInstance.getAppStatus()
// return: true | false
1
2
getOnlineToys

Get connected toy(s) information.

basicSdkInstance.getOnlineToys()

// return:
[{
  "id": "xxxxxxxx",
  "name": "Lush 3",
  "toyType": "lush",
  "nickname": "My Lush",
  "hVersion": "3",
  "fVersion": 300,
  "battery": 100,
  "connected": true
}]
1
2
3
4
5
6
7
8
9
10
11
12
13
getToys

Get toy(s) information.

Example:

basicSdkInstance.getToys()

// return:
[{
  "id": "xxxxxxxx",
  "name": "Lush 3",
  "toyType": "lush",
  "nickname": "My Lush",
  "hVersion": "3",
  "fVersion": 300,
  "battery": 100,
  "connected": true
}]
1
2
3
4
5
6
7
8
9
10
11
12
13
checkToyOnline

Check if any toys have been connected.

Example:

basicSdkInstance.checkToyOnline()
// return: true | false
1
2
getDeviceInfo

Get device Information.

basicSdkInstance.getDeviceInfo()

// return:
{
  "deviceCode": "xxxxxx",
  "domain": "192.168.1.xx.lovense.club",
  "httpsPort": 30010,
  "appVersion": "1.3.7",
  "platform": "android",
  "appType": "remote"
}
1
2
3
4
5
6
7
8
9
10
11
sendToyCommand

Send commands to toys.

Parameters:

NameTypeDescriptionRequired
vibrateNumberVibration strength, range 0-20no
rotateNumberRotation strength, range 0-20. Supported by Nora.no
pumpNumberPump strength, range 0-3. Supported by Max/Max 2.no
thrustingNumberThrusting strength, range 0-20. Supported by the Lovense Sex Machine and Gravity.no
fingeringNumberFingering strength, range 0-20. Supported by Flexer.no
suctionNumberSuction strength, range 0-20. Supported by Tenera.no
timeNumberTotal running time, 0 = indefinite length. Otherwise, the running time should be greater than 1, default 0no
toyIdStringToy ID. If you don’t include this, it will be applied to all toysno

Example:

// vibrate at 5th strength for all connected toys
basicSdkInstance.sendToyCommand({
  vibrate: 5
})

// vibrate 60 seconds at 5th strength for all connected toys
basicSdkInstance.sendToyCommand({
  vibrate: 5,
  time: 60
})

// vibrate 60 seconds at 5th strength for toys 234s25rsga3ts
// rotate 60 seconds at 10th strength for toys 234s25rsga3ts
basicSdkInstance.sendToyCommand({
  vibrate: 5,
  rotate: 10,
  time: 60,
  toyId: '234s25rsga3ts'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sendPatternCommand

Send pattern command.

Parameters:

NameTypeDescriptionRequired
strengthStringStrength pattern. Use 0-20 to form a string of numbers, separated by a semicolon. Supports up to 50 numbers, for example: 20;20;5;20;10yes
timeNumberTotal running time, 0 = indefinite length. Otherwise, running time should be greater than 1, default 0no
intervalNumberVibration intervals in milliseconds. Should be greater than 100,default 150no
vibrateBooleanWhether to enable vibration, default trueno
rotateBooleanWhether to enable rotation. Supported by Nora.no
pumpBooleanWhether to enable pump. Supported by Max/Max 2.no
thrustingBooleanWhether to enable thrusting. Supported by the Lovense Sex Machine and Gravity.no
fingeringBooleanWhether to enable fingering. Supported by Flexer.no
suctionBooleanWhether to enable suction. Supported by Tenera.no
toyIdStringToy ID. If you don’t include this, it will be applied to all toysno

Example:

basicSdkInstance.sendPatternCommand({
  strength: "6;8;10;12;14;20;20;20;16;14;12;10;8;6;6",
  time: 60
})
1
2
3
4
sendPresetCommand

Send a command from Lovense preset patterns.

NameTypeDescriptionRequired
nameStringPreset pattern name. Supports "pulse", "wave", "fireworks", "earthquake"yes
timeNumberTotal running time, 0 = indefinite length. Otherwise, running time should be greater than 1, default 0no
toyIdStringToy ID. If you don’t include this, it will be applied to all toysno

Example:

basicSdkInstance.sendPresetCommand({
  name: "pulse",
  time: 60
})
1
2
3
4
stopToyAction

Stop toy’s response.

Parameters:

NameTypeDescriptionRequired
toyIdStringToy ID. If you don’t include this, it will be applied to all toysno

Example:

basicSdkInstance.stopToyAction()
1
destroy

Destroy the instance.

Example:

basicSdkInstance.destroy()
1

Event

ready

Listen for the ready event, which will be called after successful initialization. You can use the instance normally after this event is triggered.

Example:

const basicSdkInstance = new LovenseBasicSdk({
  platform: '{platform}',
  authToken: '{authToken}',
  uid: '{uid}',
  debug: true
})
basicSdkInstance.on('ready', instance => {
  console.log('ready')
})
1
2
3
4
5
6
7
8
9
appStatusChange

Triggered when the app connection state changes. For example, users scan the QR code and establish a connection successfully.

Example:

basicSdkInstance.on('appStatusChange', status => {
  // the app connection status
  // status = true | false
})
1
2
3
4
toyInfoChange

Triggered when toy information changes.

Example:

basicSdkInstance.on('toyInfoChange', toyInfo => {
  // toyInfo:
  [{
    "id": "xxxxxxxx",
    "name": "Lush 3",
    "toyType": "lush",
    "nickname": "My Lush",
    "hVersion": "3",
    "fVersion": 300,
    "battery": 100,
    "connected": true
  }]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
toyOnlineChange

Triggered when the toy connection state changes.

Example:

basicSdkInstance.on('toyOnlineChange', status => {
  // have any toys been connected
  // status = true | false
})
1
2
3
4
deviceInfoChange

Triggered when the device information changes.

Example:

basicSdkInstance.on('deviceInfoChange', deviceInfo => {
  // deviceInfo:
  {
    "deviceCode": "xxxxxx",
    "domain": "192.168.1.xx.lovense.club",
    "httpsPort": 30010,
    "appVersion": "1.3.7",
    "platform": "android",
    "appType": "connect"
  }
})
1
2
3
4
5
6
7
8
9
10
11
Last Updated: 4/6/2023, 11:26:05 AM