Files
macrodeck-thing/main.js
2025-02-02 20:29:29 -03:00

260 lines
10 KiB
JavaScript

/*
QR code server protocol:
https://macro-deck.app/quick-setup/
+
{"instanceName":"DESKTOP-J09EM3G","networkInterfaces":["192.168.1.134"],"port":8191,"ssl":false,"token":"vPl4Vq7v"} in base64
=
https://macro-deck.app/quick-setup/eyJpbnN0YW5jZU5hbWUiOiJERVNLVE9QLUowOUVNM0ciLCJuZXR3b3JrSW50ZXJmYWNlcyI6WyIxOTIuMTY4LjEuMTM0Il0sInBvcnQiOjgxOTEsInNzbCI6ZmFsc2UsInRva2VuIjoidlBsNFZxN3YifQ==
Device types: Web - Android
Newer android app (3.3.0) identifies as web (no device settings control)
Github version (2.4.1-beta) identifies as Android (brightness, auto-connection,keep awake contrll)
Start WebSocket server
Server broadcasts connection data via UDP: {"computer-name": "MDServer","ip-address": "192.168.1.133","port": 8191}
Client gets
Client connects to websocket server
Client sends: {"Method": "CONNECTED","Client-Id": "CLIENTNAME","API": "20","Device-Type": "Android"}
Server responds with: {"Method": "GET_CONFIG","Rows": 2,"Columns": 2,"ButtonSpacing": 10,"ButtonRadius": 10,"ButtonBackground": true,"Brightness": 0.3,"AutoConnect": false,"WakeLock": "Connected","SupportButtonReleaseLongPress": true}
Client sends: {Method: 'GET_BUTTONS'}
Server responds with: {"Method": "GET_BUTTONS","Buttons": [{"IconBase64": "","Position_X": 0,"Position_Y": 0,"LabelBase64": "","BackgroundColorHex": "#232323"}]}
Client sends on button press: { "Method": "BUTTON_PRESS", "Message": "0_0" } (X_Y)
Server can respond with single button updates: {"Method": "UPDATE_BUTTON","Buttons": [{"IconBase64": "","Position_X": 0,"Position_Y": 0,"LabelBase64": "","BackgroundColorHex": "#000000"}]}
- Server can resend "GET_BUTTONS" at any moment to update setup
- Server can resend "GET_CONFIG" at any moment to control client
- Server can "UPDATE_BUTTON" in any moment
*/
function generateQR(ServerName, ServerIP, token, ServerPort = 8191) {
const qrcode = require('qrcode-terminal');
// it generates the exact same link as the official one, but it does not work (?)
var obj = {
"instanceName": ServerName,
"networkInterfaces": ServerIP,
"port": ServerPort,
"ssl": false,
"token": token
}
var url = "https://macro-deck.app/quick-setup/" + Buffer.from(JSON.stringify(obj)).toString('base64');
console.log(url)
qrcode.generate(url, { small: true }, function (qrcode) {
console.log(qrcode);
});
}
var clients = {}
function broadcastServer(ServerName, ServerIP, BroadcastPort = 8191, BroadcastAddress = '255.255.255.255') {
const dgram = require('dgram');
const udpClient = dgram.createSocket('udp4');
udpClient.bind(() => {
udpClient.setBroadcast(true);
});
setInterval(() => {
try {
const broadcastObject = {
"computer-name": ServerName,
"ip-address": ServerIP,
"port": BroadcastPort
};
const message = Buffer.from(JSON.stringify(broadcastObject));
udpClient.send(message, 0, message.length, BroadcastPort, BroadcastAddress, (err) => {
if (err) console.error(err);
});
} catch (error) {
console.error(error);
}
}, 5000);
}
function startWsServer(ServerPort = 8191) {
const { WebSocketServer } = require("ws");
const wss = new WebSocketServer({ port: ServerPort })
wss.on('connection', function connection(ws, req) {
const ip = req.socket.remoteAddress;
ws.on('error', console.error)
ws.on('message', function message(data) {
data = JSON.parse(data)
console.log(ip, data);
switch (data.Method) {
case "CONNECTED":
clients[data["Client-Id"]] = ip.replace("::ffff:", "");
//on "CONNECTED" request, send the grid setup, and client settings
var obj = JSON.stringify({
"Method": "GET_CONFIG",
"Rows": 6,
"Columns": 8,
"ButtonSpacing": 2,
"ButtonRadius": 10,
"ButtonBackground": true,
"Brightness": 0.5, // 0.1 -> 1.0
"AutoConnect": true, // true - false
"WakeLock": "Connected", //Never - Connected - Always
"SupportButtonReleaseLongPress": true
})
ws.send(obj);
break;
case "GET_BUTTONS":
//Client will request the buttons content and their position in the setup
var obj = JSON.stringify({
"Method": "GET_BUTTONS",
"Buttons": [
{
"IconBase64": "",
"Position_X": 1,
"Position_Y": 0,
"LabelBase64": "",
"BackgroundColorHex": "#232323"
},
{
"IconBase64": "",
"Position_X": 0,
"Position_Y": 0,
"LabelBase64": "",
"BackgroundColorHex": "#232323"
},
{
"IconBase64": "",
"Position_X": 1,
"Position_Y": 1,
"LabelBase64": "",
"BackgroundColorHex": "#232323"
},
{
"IconBase64": "",
"Position_X": 0,
"Position_Y": 1,
"LabelBase64": "",
"BackgroundColorHex": "#232323"
}
]
})
ws.send(obj);
break;
case "BUTTON_PRESS":
var obj = JSON.stringify({
"Method": "UPDATE_BUTTON",
"Buttons": [
{
"IconBase64": "",
"Position_X": 0,
"Position_Y": 0,
"LabelBase64": "",
"BackgroundColorHex": "#000000"
}
]
})
// broadcast update to all clients
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(obj);
}
});
var obj = JSON.stringify({
"Method": "UPDATE_BUTTON",
"Buttons": [
{
"IconBase64": "",
"Position_X": 7,
"Position_Y": 5,
"LabelBase64": "",
"BackgroundColorHex": "#000000"
}
]
})
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(obj);
}
});
//ws.send(obj)
console.log(data.Method, data.Message.split('_'))
break;
case "BUTTON_RELEASE":
var obj = JSON.stringify({
"Method": "UPDATE_BUTTON",
"Buttons": [
{
"IconBase64": "",
"Position_X": 0,
"Position_Y": 0,
"LabelBase64": "",
"BackgroundColorHex": "#FFFFFF"
}
]
})
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(obj);
}
});
var obj = JSON.stringify({
"Method": "UPDATE_BUTTON",
"Buttons": [
{
"IconBase64": "",
"Position_X": 7,
"Position_Y": 5,
"LabelBase64": "",
"BackgroundColorHex": "#FFFFFF"
}
]
})
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(obj);
}
});
//ws.send(obj);
console.log(data.Method, data.Message.split('_'))
break;
case "BUTTON_LONG_PRESS":
console.log(data.Method, data.Message.split('_'))
break;
case "BUTTON_LONG_PRESS_RELEASE":
console.log(data.Method, data.Message.split('_'))
break;
default:
console.log(data);
break;
}
});
});
}
function startWsClient(ip, port = 8191) {
const WebSocket = require("ws");
const fs = require("fs");
const ws = new WebSocket("ws://" + ip + ":" + port);
ws.on('error', console.error);
ws.on('open', function open() {
var obj = JSON.stringify({
"Method": "CONNECTED",
"Client-Id": "LinuxClient",
"API": "20",
"Device-Type": "Android" //Web - Android
});
ws.send(obj);
});
var logs = { data: [] }
ws.on('message', function message(data) {
console.log('received: %s', data);
//logs.data.push(JSON.parse(data));
//fs.writeFileSync("test.json", JSON.stringify(logs), 'utf8', null);
});
}
//generateQR("DESKTOP-J09EM3G", ["192.168.1.134"], "vPl4Vq7v");
//startWsClient("192.168.1.134");
broadcastServer("DEBIAN-PC", "192.168.1.133");
startWsServer();