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
1.2 Get IP and port
Method A:
From Lovense Remote Screen
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:
{
"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
Toy | Button | Function Strength Changed | Shake | Shake Frequency Changed | Battery Changed | Depth Changed | Motion 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 | ✅ | ✅ |