Skip to content

Commit

Permalink
🔖 Release 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
shilapi committed Jul 2, 2024
1 parent a7ba300 commit 5d6444a
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 87 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# DGLAB-V2.0-socket

一个完整的DGLAB2.0郊狼2.0 WebSocket受控端实现。基于[DGLAB-python-driver](https://github.com/shilapi/DGLAB-python-driver)

本项目的目标是让郊狼2.0主机也能用上WebSocket远控协议,降低玩家们的装备购置成本。

## 基础用法

**修改**`config.yaml`

|Key|Value|
|---|---|
|QR_Content|连接界面的二维码内容,为一串包含ws://的网址|
|Channel_A_limit|通道A强度上限|
|Channel_B_limit|通道B强度上限|
|Wave_convert_ratio|由3.0的波形转换到2.0波形的转换比例,具体请看下文转换比例一栏|
|Debug|是否开启debug模式|

**安装依赖**

```
pip3 install -r requirements
```

**运行程式**

注意:请务必在项目目录而非src目录运行,不然bug自己修

```
python3 ./src/main.py
```

## 转换比例解释

由于郊狼2.0与3.0的波形存储方式不同,2.0中由波形数据直接决定的对体感十分重要的波形频率被独立出来为一个单独的数值了。在官方的实现中,这一数值由被控者本人在app中自己设定,而WS协议并不会交换这一数值,因此我们需要预先设定一个转换比例。

简单来说,这一数值决定了你每次的电击体感是否强烈,这一数值应该介于2-20之间。**数值越小感觉越强烈。** 如若超出这一范围可能会导致体感过弱。
5 changes: 5 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
QR_Content: https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#wss://ws.dungeon-lab.cn/f5d27d08-b4c4-4a9e-a072-1f983f90425b
Channel_A_limit: 50
Channel_B_limit: 50
Wave_convert_ratio: 5
Debug: True
2 changes: 2 additions & 0 deletions requirements
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pydglab==0.3.0
websocket-client==1.8.0
126 changes: 88 additions & 38 deletions src/handler.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,146 @@
from model import *
from utils import *

from websocket import WebSocketApp
import json
import json, logging


def bind(client: WebSocketApp, message: dglab_message, store: local_data):
if message.message == "targetId":
print("Bind recieved")
logging.debug("Bind recieved")

store.targetId = message.clientId

reply = {
'type': 'bind',
'clientId': store.clientId,
'targetId': store.targetId,
'message': 'DGLAB'
"type": "bind",
"clientId": store.clientId,
"targetId": store.targetId,
"message": "DGLAB",
}

client.send(json.dumps(reply))
elif message.message == "200":
print("Bind success")
logging.info("Bind to remote success")
elif int(message.message) in code.keys():
print(code[message][0])
logging.error(code[message][0])


def heartbeat(client: WebSocketApp, message: dglab_message, store: local_data):
message = int(message.message)
if message == 200:
print("Heartbeating...")
client.send(json.dumps({
'type': 'heartbeat',
'clientId': store.clientId,
'targetId': store.targetId,
'message': 200
}))
logging.debug("Heartbeating...")
client.send(
json.dumps(
{
"type": "heartbeat",
"clientId": store.clientId,
"targetId": store.targetId,
"message": 200,
}
)
)
return
elif message in code.keys():
print(code[message][0])
logging.error(code[message][0])
return
else:
print("Unknown heartbeat message: " + message)
logging.error("Unknown heartbeat message: " + message)
raise Exception("Unknown heartbeat message: " + message)

def msg(client: WebSocketApp, message: dglab_message):

def msg(client: WebSocketApp, message: dglab_message, store: local_data):
messageType = message.message.split("-")[0]
messageContent = message.message.split("-")[1]
if messageType == "strength":
_strength(messageContent)
_strength(messageContent, store)
elif messageType == "pulse":
_pulse(messageContent)
_pulse(messageContent, store)
elif messageType == "clear":
_clear(messageContent)
_clear(messageContent, store)
pass


def strength_upload(client: WebSocketApp, message: str, store: local_data):
reply = {
'type': 'msg',
'clientId': store.clientId,
'targetId': store.targetId,
'message': message
"type": "msg",
"clientId": store.clientId,
"targetId": store.targetId,
"message": message,
}
client.send(json.dumps(reply))
pass


def break_(client: WebSocketApp, message: dglab_message):
raise Exception("Connection broken")


def error(client: WebSocketApp, message: dglab_message):
pass


def response(client: WebSocketApp, message: str, store: local_data):
reply = {
'type': 'msg',
'clientId': store.clientId,
'targetId': store.targetId,
'message': message
"type": "msg",
"clientId": store.clientId,
"targetId": store.targetId,
"message": message,
}
client.send(json.dumps(reply))
pass

def _strength(message: str):

def _strength(message: str, store: local_data):
strenthDataRaw = message.split("+")
channel = strenthDataRaw[0]
strength_mode = strenthDataRaw[1]
strength = strenthDataRaw[2]
print("Strength changing: " + channel + "--" + strength_mode + "--" + strength)
logging.debug(
"Strength changing: " + channel + "--" + strength_mode + "--" + strength
)
if channel == "1":
if strength_mode == "0":
if (
store.channelAStrength - int(strength) > 0
and store.channelAStrength - int(strength) < store.limitA
):
store.channelAStrength = store.channelAStrength - int(strength)
elif strength_mode == "1":
if (
store.channelAStrength + int(strength) > 0
and store.channelAStrength + int(strength) < store.limitA
):
store.channelAStrength = store.channelAStrength + int(strength)
elif strength_mode == "2":
if int(strength) > 0 and int(strength) < store.limitA:
store.channelAStrength = int(strength)
elif channel == "2":
if strength_mode == "0":
if (
store.channelBStrength - int(strength) > 0
and store.channelBStrength - int(strength) < store.limitB
):
store.channelBStrength = store.channelBStrength - int(strength)
elif strength_mode == "1":
if (
store.channelBStrength + int(strength) > 0
and store.channelBStrength + int(strength) < store.limitB
):
store.channelBStrength = store.channelBStrength + int(strength)
elif strength_mode == "2":
if store.channelBStrength > 0 and store.channelBStrength < store.limitB:
store.channelBStrength = int(strength)
pass

def _pulse(message: str):

def _pulse(message: str, store: local_data):
channel = message.split(":")[0]
wave_set = json.dumps(message.split(":")[1])
print("Pulse changing: " + channel +"--"+ str(wave_set))
wave_set = json.loads(message.split(":")[1])
logging.debug("Pulse changing: " + channel + "--" + str(wave_set))
dglab_wave_handler(store, wave_set, channel)
pass

def _clear(message: str):

def _clear(message: str, store: local_data):
channel = message
pass
113 changes: 72 additions & 41 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import websocket
import _thread
import time
import rel, json
import websocket, json
from model import dglab_message
from handler import *
import pydglab
from pydglab import model
import asyncio
import logging
import yaml

qrRaw = "https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#wss://ws.dungeon-lab.cn/7f6238c1-fed1-41b0-a697-4d68ce914cfc"
with open("./config.yaml") as stream:
try:
config = yaml.safe_load(stream)
except yaml.YAMLError as exc:
logging.error(exc)

store = local_data(clientId=qrRaw[82:])

dglab = pydglab.dglab()
asyncio.run(dglab.create())

def converter_to_waveset(self, strength: int, freq: int) -> tuple[int, int, int]:
"""
Convert strength and frequency to the wave set format.
"""
wave1 = int(((freq - 10) / 230) * 990 + 10 - wave2)
wave2 = int(strength / 5)
wave3 = strength
logging.basicConfig(
format="%(module)s [%(levelname)s]: %(message)s",
level=logging.DEBUG if config["Debug"] else logging.INFO,
)

return wave1, wave2, wave3
qrRaw = config["QR_Content"]

async def dglab_handler():
await dglab.set_strength_sync(store.channelAStrength, store.channelBStrength)
await dglab.set_wave_set_sync
store = local_data(clientId=qrRaw[82:])
store.limitA = int(config["Channel_A_limit"])
store.limitB = int(config["Channel_B_limit"])

async def dglab_wave_handler(wave: list[(tuple[int, int, int])]):
for i in wave:

def on_message(ws, message):
#print(message)
message = json.loads(message)
message = dglab_message(type_=message['type'], clientId=message['clientId'], targetId=message['targetId'], message=message['message'])
message = dglab_message(
type_=message["type"],
clientId=message["clientId"],
targetId=message["targetId"],
message=message["message"],
)
if message.type_ == "heartbeat":
heartbeat(ws, message, store)
return
Expand All @@ -44,30 +40,65 @@ def on_message(ws, message):
strength_upload(ws, f"strength-0+0+{store.limitB}+{store.limitA}", store=store)
return
elif message.type_ == "msg":
msg(ws, message)
msg(ws, message, store)
elif message.type_ == "break":
break_(ws, message)
elif message.type_ == "error":
error(ws, message)
response(ws, 'feedback-0', store)
response(ws, "feedback-0", store)


def on_error(ws, error):
print(error)
logging.error(error)


def on_close(ws, close_status_code, close_msg):
print("### closed ###")
logging.info("WS connection closed")


def on_open(ws):
print("Opened connection")

logging.info("WS connection opened")

if __name__ == "__main__":

def run_websocket():
websocket.enableTrace(False)
ws = websocket.WebSocketApp(qrRaw[58:],
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever(dispatcher=rel, reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if connection closed unexpectedly
rel.signal(2, rel.abort) # Keyboard Interrupt
rel.dispatch()
ws = websocket.WebSocketApp(
qrRaw[58:],
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close,
)
ws.run_forever(
reconnect=5
)


async def main():
dglab = pydglab.dglab()
await dglab.create()

async def dglab_handler():
while True:
if store.channelAStrength > store.limitA or store.channelAStrength < 0:
store.channelAStrength = 0
if store.channelBStrength > store.limitB or store.channelBStrength < 0:
store.channelBStrength = 0
await dglab.set_strength_sync(
store.channelAStrength, store.channelBStrength
)
await dglab.set_wave_set_sync(store.channelAWave, store.channelBWave)
await asyncio.sleep(0.1)

dglab_task = asyncio.create_task(dglab_handler())

# Run the synchronous function run_websocket in the event loop without blocking
loop = asyncio.get_running_loop()
websocket_task = loop.run_in_executor(None, run_websocket)

# Wait for both tasks to complete
await asyncio.gather(dglab_task, websocket_task)


if __name__ == "__main__":
asyncio.run(main())
Loading

0 comments on commit 5d6444a

Please sign in to comment.