From f65ad45701f009740f02a017de82da2b37102969 Mon Sep 17 00:00:00 2001 From: Nikola <1388673+jurkovic-nikola@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:15:17 +0100 Subject: [PATCH] K70 RGB PRO --- README.md | 1 + database/keyboard/k55core-eu.json | 2 +- database/keyboard/k55core.json | 2 +- database/keyboard/k70core-eu.json | 2 +- database/keyboard/k70core.json | 2 +- database/keyboard/k70pro-eu.json | 1513 ++++++++++++++++++++++++++ database/keyboard/k70pro.json | 1513 ++++++++++++++++++++++++++ src/devices/devices.go | 96 +- src/devices/k70pro/k70pro.go | 1073 ++++++++++++++++++ src/keyboards/keyboards.go | 1 + src/server/requests/requests.go | 2 +- src/templates/templates.go | 1 + static/img/icons/icon-backward.svg | 1 + static/img/icons/icon-brightness.svg | 1 + static/img/icons/icon-forward.svg | 1 + static/img/icons/icon-lock.svg | 1 + static/img/icons/icon-mute.svg | 1 + static/img/icons/icon-pause.svg | 1 + static/img/icons/icon-stop.svg | 1 + static/img/icons/icon-user.svg | 1 + web/devices.html | 2 + web/k70pro.html | 258 +++++ 22 files changed, 4469 insertions(+), 7 deletions(-) create mode 100644 database/keyboard/k70pro-eu.json create mode 100644 database/keyboard/k70pro.json create mode 100644 src/devices/k70pro/k70pro.go create mode 100644 static/img/icons/icon-backward.svg create mode 100644 static/img/icons/icon-brightness.svg create mode 100644 static/img/icons/icon-forward.svg create mode 100644 static/img/icons/icon-lock.svg create mode 100644 static/img/icons/icon-mute.svg create mode 100644 static/img/icons/icon-pause.svg create mode 100644 static/img/icons/icon-stop.svg create mode 100644 static/img/icons/icon-user.svg create mode 100644 web/k70pro.html diff --git a/README.md b/README.md index 4192fa3..60556a2 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Open source Linux interface for iCUE LINK Hub and other Corsair AIOs, Hubs. | K55 CORE RGB | `1b1c` | `1bfe` | | | K65 PRO MINI | `1b1c` | `1bd7` | | | K70 CORE RGB | `1b1c` | `1bfd` | | +| K70 PRO RGB | `1b1c` | `1bc6` | | ## Installation (automatic) 1. Download either .deb or .rpm package from the latest Release, depends on your Linux distribution diff --git a/database/keyboard/k55core-eu.json b/database/keyboard/k55core-eu.json index 0e8742e..9a4d979 100644 --- a/database/keyboard/k55core-eu.json +++ b/database/keyboard/k55core-eu.json @@ -2,7 +2,7 @@ "key": "k55core-default", "device": "K55 Core RGB", "layout": "EU", - "rows": 5, + "rows": 6, "zones": { "1":{ "color": { diff --git a/database/keyboard/k55core.json b/database/keyboard/k55core.json index e03d930..1e3780f 100644 --- a/database/keyboard/k55core.json +++ b/database/keyboard/k55core.json @@ -2,7 +2,7 @@ "key": "k55core-default", "device": "K55 Core RGB", "layout": "US", - "rows": 5, + "rows": 6, "zones": { "1":{ "color": { diff --git a/database/keyboard/k70core-eu.json b/database/keyboard/k70core-eu.json index a9c336e..955d94b 100644 --- a/database/keyboard/k70core-eu.json +++ b/database/keyboard/k70core-eu.json @@ -2,7 +2,7 @@ "key": "k70core-default", "device": "K70 Core RGB", "layout": "EU", - "rows": 5, + "rows": 6, "row": { "0": { "keys": { diff --git a/database/keyboard/k70core.json b/database/keyboard/k70core.json index 4cf8de6..669d44f 100644 --- a/database/keyboard/k70core.json +++ b/database/keyboard/k70core.json @@ -2,7 +2,7 @@ "key": "k70core-default", "device": "K70 Core RGB", "layout": "US", - "rows": 5, + "rows": 6, "row": { "0": { "keys": { diff --git a/database/keyboard/k70pro-eu.json b/database/keyboard/k70pro-eu.json new file mode 100644 index 0000000..76be740 --- /dev/null +++ b/database/keyboard/k70pro-eu.json @@ -0,0 +1,1513 @@ +{ + "key": "k70pro-default", + "device": "K70 Pro RGB", + "layout": "US", + "rows": 6, + "row": { + "0": { + "keys": { + "1": { + "keyName": "icon-user.svg", + "width": 65, + "height": 40, + "left": 0, + "top": 0, + "packetIndex": [384], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "2": { + "keyName": "icon-brightness.svg", + "width": 65, + "height": 40, + "left": 15, + "top": 0, + "packetIndex": [339], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "3": { + "keyName": "icon-lock.svg", + "width": 65, + "height": 40, + "left": 15, + "top": 0, + "packetIndex": [342], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "4": { + "keyName": "", + "width": 404, + "height": 40, + "left": 400, + "top": 0, + "packetIndex": [411,414], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "5": { + "keyName": "icon-mute.svg", + "width": 65, + "height": 40, + "left": 515, + "top": 0, + "packetIndex": [306], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + } + } + }, + "1": { + "keys": { + "6": { + "keyName": "ESC", + "width": 65, + "height": 70, + "left": 0, + "top": 50, + "packetIndex": [123], + "color": { + "red": 0, + "green": 255, + "blue": 0 + } + }, + "7": { + "keyName": "F1", + "width": 65, + "height": 70, + "left": 95, + "top": 50, + "packetIndex": [174], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "8": { + "keyName": "F2", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [177], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "9": { + "keyName": "F3", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [180], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "10": { + "keyName": "F4", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [183], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "11": { + "keyName": "F5", + "width": 65, + "height": 70, + "left": 55, + "top": 50, + "packetIndex": [186], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "12": { + "keyName": "F6", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [189], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "13": { + "keyName": "F7", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [192], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "14": { + "keyName": "F8", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [195], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "15": { + "keyName": "F9", + "width": 65, + "height": 70, + "left": 60, + "top": 50, + "packetIndex": [198], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "16": { + "keyName": "F10", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [201], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "17": { + "keyName": "F11", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [204], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "18": { + "keyName": "F12", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [207], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "19": { + "keyName": "PrtSc", + "width": 65, + "height": 70, + "left": 25, + "top": 50, + "packetIndex": [210], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "20": { + "keyName": "ScrLk", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [213], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "21": { + "keyName": "Pause", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [216], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "22": { + "keyName": "icon-stop.svg", + "width": 65, + "height": 70, + "left": 25, + "top": 50, + "packetIndex": [369], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "23": { + "keyName": "icon-backward.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [378], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "24": { + "keyName": "icon-pause.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [372], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "25": { + "keyName": "icon-forward.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [375], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + } + } + }, + "2": { + "keys": { + "26": { + "keyName": "` ~", + "width": 65, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [159], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "27": { + "keyName": "1", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [90], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "28": { + "keyName": "2", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [93], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "29": { + "keyName": "3", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [96], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "30": { + "keyName": "4", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [99], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "31": { + "keyName": "5", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [102], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "32": { + "keyName": "6", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [105], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "33": { + "keyName": "7", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [108], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "34": { + "keyName": "8", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [111], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "35": { + "keyName": "9", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [114], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "36": { + "keyName": "0", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [117], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "37": { + "keyName": "-", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [135], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "38": { + "keyName": "=", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [138], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "39": { + "keyName": "<---", + "width": 150, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [126], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "40": { + "keyName": "Ins", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [219], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "41": { + "keyName": "Home", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [222], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "42": { + "keyName": "PgUp", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [225], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "43": { + "keyName": "Num", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [249], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "44": { + "keyName": "/", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [252], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "45": { + "keyName": "*", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [255], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "46": { + "keyName": "-", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [258], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "3": { + "keys": { + "47": { + "keyName": "Tab", + "width": 108, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [129], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "48": { + "keyName": "Q", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [60], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "49": { + "keyName": "W", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [78], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "50": { + "keyName": "E", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [24], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "51": { + "keyName": "R", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [63], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "52": { + "keyName": "T", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [69], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "53": { + "keyName": "Z", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [84], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "54": { + "keyName": "U", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [72], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "55": { + "keyName": "I", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [36], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "56": { + "keyName": "O", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [54], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "57": { + "keyName": "P", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [57], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "58": { + "keyName": "[ {", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [141], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "59": { + "keyName": "] }", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [144], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "60": { + "keyName": "\\ |", + "width": 107, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [147], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "61": { + "keyName": "Del", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [228], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "62": { + "keyName": "End", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [231], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "63": { + "keyName": "PgDn", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [234], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "64": { + "keyName": "7", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [285], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "65": { + "keyName": "8", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [288], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "66": { + "keyName": "9", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [291], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "67": { + "keyName": "+", + "width": 65, + "height": 155, + "left": 15, + "top": 15, + "packetIndex": [261], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "4": { + "keys": { + "68": { + "keyName": "CAPS", + "width": 131, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [171], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "69": { + "keyName": "A", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [12], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "70": { + "keyName": "S", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [66], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "71": { + "keyName": "D", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [21], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "72": { + "keyName": "F", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [27], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "73": { + "keyName": "G", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [30], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "74": { + "keyName": "H", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [33], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "75": { + "keyName": "J", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [39], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "76": { + "keyName": "K", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [42], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "77": { + "keyName": "L", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [45], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "78": { + "keyName": "; :", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [153], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "79": { + "keyName": "' ''", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [156], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "80": { + "keyName": "Enter", + "width": 164, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [120], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "81": { + "keyName": "4", + "width": 65, + "height": 70, + "left": 275, + "top": 15, + "packetIndex": [276], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "82": { + "keyName": "5", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [279], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "83": { + "keyName": "6", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [282], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "5": { + "keys": { + "84": { + "keyName": "Shift", + "width": 170, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [318], + "color": { + "red": 255, + "green": 255, + "blue": 0 + } + }, + "85": { + "keyName": "Y", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [87], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "86": { + "keyName": "X", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [81], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "87": { + "keyName": "C", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [18], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "88": { + "keyName": "V", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [75], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "89": { + "keyName": "B", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [15], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "90": { + "keyName": "N", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [51], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "91": { + "keyName": "M", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [48], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "92": { + "keyName": ", <", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [162], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "93": { + "keyName": ". >", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [165], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "94": { + "keyName": "/ ?", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [168], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "95": { + "keyName": "Shift", + "width": 205, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [330], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "96": { + "keyName": "↑", + "width": 65, + "height": 70, + "left": 105, + "top": 15, + "packetIndex": [246], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "97": { + "keyName": "1", + "width": 65, + "height": 70, + "left": 105, + "top": 15, + "packetIndex": [267], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "98": { + "keyName": "2", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [270], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "99": { + "keyName": "3", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [273], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "100": { + "keyName": "Enter", + "width": 65, + "height": 155, + "left": 15, + "top": 15, + "packetIndex": [264], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "6": { + "keys": { + "101": { + "keyName": "Ctrl", + "width": 90, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [315], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "102": { + "keyName": "⊞", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [324], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "103": { + "keyName": "Alt", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [321], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "104": { + "keyName": "----------", + "width": 455, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [132], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "105": { + "keyName": "Alt", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [333], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "106": { + "keyName": "Fn", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [366], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "107": { + "keyName": "RC", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [303], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "108": { + "keyName": "Ctrl", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [327], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "109": { + "keyName": "←", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [240], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "110": { + "keyName": "↓", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [243], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "111": { + "keyName": "→", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [237], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "112": { + "keyName": "0", + "width": 145, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [294], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "113": { + "keyName": ".", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [297], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + } + } +} \ No newline at end of file diff --git a/database/keyboard/k70pro.json b/database/keyboard/k70pro.json new file mode 100644 index 0000000..c798cb8 --- /dev/null +++ b/database/keyboard/k70pro.json @@ -0,0 +1,1513 @@ +{ + "key": "k70pro-default", + "device": "K70 Pro RGB", + "layout": "US", + "rows": 6, + "row": { + "0": { + "keys": { + "1": { + "keyName": "icon-user.svg", + "width": 65, + "height": 40, + "left": 0, + "top": 0, + "packetIndex": [384], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "2": { + "keyName": "icon-brightness.svg", + "width": 65, + "height": 40, + "left": 15, + "top": 0, + "packetIndex": [339], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "3": { + "keyName": "icon-lock.svg", + "width": 65, + "height": 40, + "left": 15, + "top": 0, + "packetIndex": [342], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "4": { + "keyName": "", + "width": 404, + "height": 40, + "left": 400, + "top": 0, + "packetIndex": [411,414], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "5": { + "keyName": "icon-mute.svg", + "width": 65, + "height": 40, + "left": 515, + "top": 0, + "packetIndex": [306], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + } + } + }, + "1": { + "keys": { + "6": { + "keyName": "ESC", + "width": 65, + "height": 70, + "left": 0, + "top": 50, + "packetIndex": [123], + "color": { + "red": 0, + "green": 255, + "blue": 0 + } + }, + "7": { + "keyName": "F1", + "width": 65, + "height": 70, + "left": 95, + "top": 50, + "packetIndex": [174], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "8": { + "keyName": "F2", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [177], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "9": { + "keyName": "F3", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [180], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "10": { + "keyName": "F4", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [183], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "11": { + "keyName": "F5", + "width": 65, + "height": 70, + "left": 55, + "top": 50, + "packetIndex": [186], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "12": { + "keyName": "F6", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [189], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "13": { + "keyName": "F7", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [192], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "14": { + "keyName": "F8", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [195], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "15": { + "keyName": "F9", + "width": 65, + "height": 70, + "left": 60, + "top": 50, + "packetIndex": [198], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "16": { + "keyName": "F10", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [201], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "17": { + "keyName": "F11", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [204], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "18": { + "keyName": "F12", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [207], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "19": { + "keyName": "PrtSc", + "width": 65, + "height": 70, + "left": 25, + "top": 50, + "packetIndex": [210], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "20": { + "keyName": "ScrLk", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [213], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "21": { + "keyName": "Pause", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [216], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "22": { + "keyName": "icon-stop.svg", + "width": 65, + "height": 70, + "left": 25, + "top": 50, + "packetIndex": [369], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "23": { + "keyName": "icon-backward.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [378], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "24": { + "keyName": "icon-pause.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [372], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + }, + "25": { + "keyName": "icon-forward.svg", + "width": 65, + "height": 70, + "left": 15, + "top": 50, + "packetIndex": [375], + "color": { + "red": 0, + "green": 255, + "blue": 255 + }, + "svg": true + } + } + }, + "2": { + "keys": { + "26": { + "keyName": "` ~", + "width": 65, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [159], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "27": { + "keyName": "1", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [90], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "28": { + "keyName": "2", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [93], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "29": { + "keyName": "3", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [96], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "30": { + "keyName": "4", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [99], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "31": { + "keyName": "5", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [102], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "32": { + "keyName": "6", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [105], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "33": { + "keyName": "7", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [108], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "34": { + "keyName": "8", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [111], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "35": { + "keyName": "9", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [114], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "36": { + "keyName": "0", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [117], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "37": { + "keyName": "-", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [135], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "38": { + "keyName": "=", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [138], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "39": { + "keyName": "<---", + "width": 150, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [126], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "40": { + "keyName": "Ins", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [219], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "41": { + "keyName": "Home", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [222], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "42": { + "keyName": "PgUp", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [225], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "43": { + "keyName": "Num", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [249], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "44": { + "keyName": "/", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [252], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "45": { + "keyName": "*", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [255], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "46": { + "keyName": "-", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [258], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "3": { + "keys": { + "47": { + "keyName": "Tab", + "width": 108, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [129], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "48": { + "keyName": "Q", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [60], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "49": { + "keyName": "W", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [78], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "50": { + "keyName": "E", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [24], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "51": { + "keyName": "R", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [63], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "52": { + "keyName": "T", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [69], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "53": { + "keyName": "Y", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [84], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "54": { + "keyName": "U", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [72], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "55": { + "keyName": "I", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [36], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "56": { + "keyName": "O", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [54], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "57": { + "keyName": "P", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [57], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "58": { + "keyName": "[ {", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [141], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "59": { + "keyName": "] }", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [144], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "60": { + "keyName": "\\ |", + "width": 107, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [147], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "61": { + "keyName": "Del", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [228], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "62": { + "keyName": "End", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [231], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "63": { + "keyName": "PgDn", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [234], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "64": { + "keyName": "7", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [285], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "65": { + "keyName": "8", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [288], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "66": { + "keyName": "9", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [291], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "67": { + "keyName": "+", + "width": 65, + "height": 155, + "left": 15, + "top": 15, + "packetIndex": [261], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "4": { + "keys": { + "68": { + "keyName": "CAPS", + "width": 131, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [171], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "69": { + "keyName": "A", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [12], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "70": { + "keyName": "S", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [66], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "71": { + "keyName": "D", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [21], + "color": { + "red": 255, + "green": 0, + "blue": 0 + } + }, + "72": { + "keyName": "F", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [27], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "73": { + "keyName": "G", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [30], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "74": { + "keyName": "H", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [33], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "75": { + "keyName": "J", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [39], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "76": { + "keyName": "K", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [42], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "77": { + "keyName": "L", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [45], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "78": { + "keyName": "; :", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [153], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "79": { + "keyName": "' ''", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [156], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "80": { + "keyName": "Enter", + "width": 164, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [120], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "81": { + "keyName": "4", + "width": 65, + "height": 70, + "left": 275, + "top": 15, + "packetIndex": [276], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "82": { + "keyName": "5", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [279], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "83": { + "keyName": "6", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [282], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "5": { + "keys": { + "84": { + "keyName": "Shift", + "width": 170, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [318], + "color": { + "red": 255, + "green": 255, + "blue": 0 + } + }, + "85": { + "keyName": "Z", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [87], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "86": { + "keyName": "X", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [81], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "87": { + "keyName": "C", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [18], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "88": { + "keyName": "V", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [75], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "89": { + "keyName": "B", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [15], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "90": { + "keyName": "N", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [51], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "91": { + "keyName": "M", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [48], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "92": { + "keyName": ", <", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [162], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "93": { + "keyName": ". >", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [165], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "94": { + "keyName": "/ ?", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [168], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "95": { + "keyName": "Shift", + "width": 205, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [330], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "96": { + "keyName": "↑", + "width": 65, + "height": 70, + "left": 105, + "top": 15, + "packetIndex": [246], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "97": { + "keyName": "1", + "width": 65, + "height": 70, + "left": 105, + "top": 15, + "packetIndex": [267], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "98": { + "keyName": "2", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [270], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "99": { + "keyName": "3", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [273], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "100": { + "keyName": "Enter", + "width": 65, + "height": 155, + "left": 15, + "top": 15, + "packetIndex": [264], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + }, + "6": { + "keys": { + "101": { + "keyName": "Ctrl", + "width": 90, + "height": 70, + "left": 0, + "top": 15, + "packetIndex": [315], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "102": { + "keyName": "⊞", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [324], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "103": { + "keyName": "Alt", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [321], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "104": { + "keyName": "----------", + "width": 455, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [132], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "105": { + "keyName": "Alt", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [333], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "106": { + "keyName": "Fn", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [366], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "107": { + "keyName": "RC", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [303], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "108": { + "keyName": "Ctrl", + "width": 90, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [327], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "109": { + "keyName": "←", + "width": 65, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [240], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "110": { + "keyName": "↓", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [243], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "111": { + "keyName": "→", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [237], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "112": { + "keyName": "0", + "width": 145, + "height": 70, + "left": 25, + "top": 15, + "packetIndex": [294], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + }, + "113": { + "keyName": ".", + "width": 65, + "height": 70, + "left": 15, + "top": 15, + "packetIndex": [297], + "color": { + "red": 0, + "green": 255, + "blue": 255 + } + } + } + } + } +} \ No newline at end of file diff --git a/src/devices/devices.go b/src/devices/devices.go index 1d270a3..d0beae0 100644 --- a/src/devices/devices.go +++ b/src/devices/devices.go @@ -9,6 +9,7 @@ import ( "OpenLinkHub/src/devices/k55core" "OpenLinkHub/src/devices/k65pm" "OpenLinkHub/src/devices/k70core" + "OpenLinkHub/src/devices/k70pro" "OpenLinkHub/src/devices/lncore" "OpenLinkHub/src/devices/lnpro" "OpenLinkHub/src/devices/lsh" @@ -36,6 +37,7 @@ const ( productTypeK65PM = 101 productTypeK70Core = 102 productTypeK55Core = 103 + productTypeK70Pro = 104 ) type AIOData struct { @@ -61,7 +63,9 @@ type Device struct { K65PM *k65pm.Device `json:"k65PM,omitempty"` K70Core *k70core.Device `json:"k70core,omitempty"` K55Core *k55core.Device `json:"k55core,omitempty"` - GetDevice interface{} + K70Pro *k70pro.Device `json:"k70pro,omitempty"` + + GetDevice interface{} } var ( @@ -69,7 +73,7 @@ var ( interfaceId = 0 devices = make(map[string]*Device, 0) products = make(map[string]uint16) - keyboards = []uint16{7127, 7165, 7166} + keyboards = []uint16{7127, 7165, 7166, 7110} ) // Stop will stop all active devices @@ -148,6 +152,12 @@ func Stop() { device.K55Core.Stop() } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + device.K70Pro.Stop() + } + } } } err := hid.Exit() @@ -172,6 +182,12 @@ func UpdateKeyboardColor(deviceId string, keyId, keyOptions int, color rgb.Color return device.K70Core.UpdateDeviceColor(keyId, keyOptions, color) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.UpdateDeviceColor(keyId, keyOptions, color) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -331,6 +347,12 @@ func SaveKeyboardProfile(deviceId, profileName string, new bool) uint8 { return device.K70Core.SaveKeyboardProfile(profileName, new) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.SaveKeyboardProfile(profileName, new) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -358,6 +380,12 @@ func ChangeKeyboardLayout(deviceId, layout string) uint8 { return device.K70Core.ChangeKeyboardLayout(layout) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.ChangeKeyboardLayout(layout) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -385,6 +413,12 @@ func ChangeKeyboardProfile(deviceId, profileName string) uint8 { return device.K70Core.UpdateKeyboardProfile(profileName) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.UpdateKeyboardProfile(profileName) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -412,6 +446,12 @@ func DeleteKeyboardProfile(deviceId, profileName string) uint8 { return device.K70Core.DeleteKeyboardProfile(profileName) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.DeleteKeyboardProfile(profileName) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -493,6 +533,12 @@ func SaveUserProfile(deviceId, profileName string) uint8 { return device.K70Core.SaveUserProfile(profileName) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.SaveUserProfile(profileName) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -589,6 +635,12 @@ func ChangeDeviceBrightness(deviceId string, mode uint8) uint8 { return device.K70Core.ChangeDeviceBrightness(mode) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.ChangeDeviceBrightness(mode) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -670,6 +722,12 @@ func ChangeUserProfile(deviceId, profileName string) uint8 { return device.K70Core.ChangeDeviceProfile(profileName) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.ChangeDeviceProfile(profileName) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -828,6 +886,12 @@ func UpdateDeviceLabel(deviceId string, channelId int, label string, deviceType return device.K70Core.UpdateDeviceLabel(label) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.UpdateDeviceLabel(label) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -1002,6 +1066,12 @@ func UpdateRgbProfile(deviceId string, channelId int, profile string) uint8 { return device.K70Core.UpdateRgbProfile(profile) } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro.UpdateRgbProfile(profile) + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -1148,6 +1218,12 @@ func GetDevice(deviceId string) interface{} { return device.K70Core } } + case productTypeK70Pro: + { + if device.K70Pro != nil { + return device.K70Pro + } + } case productTypeK55Core: { if device.K55Core != nil { @@ -1394,6 +1470,22 @@ func Init() { } }(vendorId, productId, key) } + case 7110: // K70 RGB PRO + { + go func(vendorId, productId uint16, key string) { + dev := k70pro.Init(vendorId, productId, key) + if dev == nil { + return + } + devices[dev.Serial] = &Device{ + K70Pro: dev, + ProductType: productTypeK70Pro, + Product: dev.Product, + Serial: dev.Serial, + Firmware: dev.Firmware, + } + }(vendorId, productId, key) + } case 0: // Memory { go func(serialId string) { diff --git a/src/devices/k70pro/k70pro.go b/src/devices/k70pro/k70pro.go new file mode 100644 index 0000000..aed890a --- /dev/null +++ b/src/devices/k70pro/k70pro.go @@ -0,0 +1,1073 @@ +package k70pro + +import ( + "OpenLinkHub/src/common" + "OpenLinkHub/src/config" + "OpenLinkHub/src/keyboards" + "OpenLinkHub/src/logger" + "OpenLinkHub/src/rgb" + "OpenLinkHub/src/temperatures" + "encoding/binary" + "encoding/json" + "fmt" + "github.com/sstallion/go-hid" + "os" + "regexp" + "slices" + "strings" + "sync" + "time" +) + +// Package: K65 Pro Mini +// This is the primary package for K65 Pro Mini. +// All device actions are controlled from this package. +// Author: Nikola Jurkovic +// License: GPL-3.0 or later + +// DeviceProfile struct contains all device profile +type DeviceProfile struct { + Active bool + Path string + Product string + Serial string + LCDMode uint8 + LCDRotation uint8 + Brightness uint8 + RGBProfile string + Label string + Layout string + Keyboards map[string]*keyboards.Keyboard + Profile string + Profiles []string +} + +type Device struct { + Debug bool + dev *hid.Device + Manufacturer string `json:"manufacturer"` + Product string `json:"product"` + Serial string `json:"serial"` + Firmware string `json:"firmware"` + activeRgb *rgb.ActiveRGB + UserProfiles map[string]*DeviceProfile `json:"userProfiles"` + Devices map[int]string `json:"devices"` + DeviceProfile *DeviceProfile + OriginalProfile *DeviceProfile + Template string + VendorId uint16 + Brightness map[int]string + LEDChannels int + CpuTemp float32 + GpuTemp float32 + Layouts []string +} + +var ( + pwd = "" + cmdSoftwareMode = []byte{0x01, 0x03, 0x00, 0x02} + cmdHardwareMode = []byte{0x01, 0x03, 0x00, 0x01} + cmdActivateLed = []byte{0x0d, 0x01, 0x22} + cmdGetFirmware = []byte{0x02, 0x13} + dataTypeSetColor = []byte{0x12, 0x00} + dataTypeSubColor = []byte{0x07, 0x00} + cmdWriteColor = []byte{0x06, 0x01} + deviceRefreshInterval = 1000 + timer = &time.Ticker{} + authRefreshChan = make(chan bool) + mutex sync.Mutex + transferTimeout = 500 + bufferSize = 1024 + bufferSizeWrite = bufferSize + 1 + headerSize = 2 + headerWriteSize = 4 + maxBufferSizePerRequest = 1021 + colorPacketLength = 428 + keyboardKey = "k70pro-default" + defaultLayout = "k70pro-default-US" +) + +// Stop will stop all device operations and switch a device back to hardware mode +func (d *Device) Stop() { + logger.Log(logger.Fields{"serial": d.Serial}).Info("Stopping device...") + if d.activeRgb != nil { + d.activeRgb.Stop() + } + timer.Stop() + authRefreshChan <- true + + d.setHardwareMode() + if d.dev != nil { + err := d.dev.Close() + if err != nil { + logger.Log(logger.Fields{"error": err}).Error("Unable to close HID device") + } + } +} + +func Init(vendorId, productId uint16, key string) *Device { + // Set global working directory + pwd = config.GetConfig().ConfigPath + + dev, err := hid.OpenPath(key) + if err != nil { + logger.Log(logger.Fields{"error": err, "vendorId": vendorId, "productId": productId}).Error("Unable to open HID device") + return nil + } + + // Init new struct with HID device + d := &Device{ + dev: dev, + Template: "k70pro.html", + VendorId: vendorId, + Brightness: map[int]string{ + 0: "RGB Profile", + 1: "33 %", + 2: "66 %", + 3: "100 %", + }, + Product: "K70 Pro RGB", + LEDChannels: 142, + Layouts: keyboards.GetLayouts(keyboardKey), + } + + d.getDebugMode() // Debug mode + d.getManufacturer() // Manufacturer + d.getSerial() // Serial + d.setSoftwareMode() // Activate software mode + d.initLeds() // Init LED ports + d.getDeviceFirmware() // Firmware + d.loadDeviceProfiles() // Load all device profiles + d.saveDeviceProfile() // Save profile + d.setAutoRefresh() // Set auto device refresh + d.setDeviceColor() // Device color + return d +} + +// getManufacturer will return device manufacturer +func (d *Device) getDebugMode() { + d.Debug = config.GetConfig().Debug +} + +// getManufacturer will return device manufacturer +func (d *Device) getManufacturer() { + manufacturer, err := d.dev.GetMfrStr() + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to get manufacturer") + } + d.Manufacturer = manufacturer +} + +// getProduct will return device name +func (d *Device) getProduct() { + product, err := d.dev.GetProductStr() + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to get product") + } + d.Product = product +} + +// getSerial will return device serial number +func (d *Device) getSerial() { + serial, err := d.dev.GetSerialNbr() + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to get device serial number") + } + d.Serial = serial +} + +// setHardwareMode will switch a device to hardware mode +func (d *Device) setHardwareMode() { + _, err := d.transfer(cmdHardwareMode, nil) + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to change device mode") + } +} + +// setSoftwareMode will switch a device to software mode +func (d *Device) setSoftwareMode() { + _, err := d.transfer(cmdSoftwareMode, nil) + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to change device mode") + } +} + +// getDeviceFirmware will return a device firmware version out as string +func (d *Device) getDeviceFirmware() { + fw, err := d.transfer( + cmdGetFirmware, + nil, + ) + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to write to a device") + } + + v1, v2, v3 := int(fw[3]), int(fw[4]), int(binary.LittleEndian.Uint16(fw[5:7])) + d.Firmware = fmt.Sprintf("%d.%d.%d", v1, v2, v3) +} + +// initLeds will initialize LED ports +func (d *Device) initLeds() { + _, err := d.transfer(cmdActivateLed, nil) + if err != nil { + logger.Log(logger.Fields{"error": err}).Fatal("Unable to change device mode") + } + // We need to wait around 500 ms for physical ports to re-initialize + // After that we can grab any new connected / disconnected device values + time.Sleep(time.Duration(transferTimeout) * time.Millisecond) +} + +// saveDeviceProfile will save device profile for persistent configuration +func (d *Device) saveDeviceProfile() { + profilePath := pwd + "/database/profiles/" + d.Serial + ".json" + keyboardMap := make(map[string]*keyboards.Keyboard, 0) + + deviceProfile := &DeviceProfile{ + Product: d.Product, + Serial: d.Serial, + Path: profilePath, + } + + // First save, assign saved profile to a device + if d.DeviceProfile == nil { + // RGB, Label + deviceProfile.RGBProfile = "keyboard" + deviceProfile.Label = "Keyboard" + deviceProfile.Active = true + keyboardMap["default"] = keyboards.GetKeyboard(defaultLayout) + deviceProfile.Keyboards = keyboardMap + deviceProfile.Profile = "default" + deviceProfile.Profiles = []string{"default"} + deviceProfile.Layout = "US" + } else { + if len(d.DeviceProfile.Layout) == 0 { + deviceProfile.Layout = "US" + } else { + deviceProfile.Layout = d.DeviceProfile.Layout + } + deviceProfile.Active = d.DeviceProfile.Active + deviceProfile.Brightness = d.DeviceProfile.Brightness + deviceProfile.RGBProfile = d.DeviceProfile.RGBProfile + deviceProfile.Label = d.DeviceProfile.Label + deviceProfile.Profile = d.DeviceProfile.Profile + deviceProfile.Profiles = d.DeviceProfile.Profiles + deviceProfile.Keyboards = d.DeviceProfile.Keyboards + if len(d.DeviceProfile.Path) < 1 { + deviceProfile.Path = profilePath + d.DeviceProfile.Path = profilePath + } else { + deviceProfile.Path = d.DeviceProfile.Path + } + deviceProfile.LCDMode = d.DeviceProfile.LCDMode + deviceProfile.LCDRotation = d.DeviceProfile.LCDRotation + } + + // Convert to JSON + buffer, err := json.MarshalIndent(deviceProfile, "", " ") + if err != nil { + logger.Log(logger.Fields{"error": err}).Error("Unable to convert to json format") + return + } + + // Create profile filename + file, fileErr := os.Create(deviceProfile.Path) + if fileErr != nil { + logger.Log(logger.Fields{"error": err, "location": deviceProfile.Path}).Error("Unable to create new device profile") + return + } + + // Write JSON buffer to file + _, err = file.Write(buffer) + if err != nil { + logger.Log(logger.Fields{"error": err, "location": deviceProfile.Path}).Error("Unable to write data") + return + } + + // Close file + err = file.Close() + if err != nil { + logger.Log(logger.Fields{"error": err, "location": deviceProfile.Path}).Fatal("Unable to close file handle") + } + + d.loadDeviceProfiles() // Reload +} + +// loadDeviceProfiles will load custom user profiles +func (d *Device) loadDeviceProfiles() { + profileList := make(map[string]*DeviceProfile, 0) + userProfileDirectory := pwd + "/database/profiles/" + + files, err := os.ReadDir(userProfileDirectory) + if err != nil { + logger.Log(logger.Fields{"error": err, "location": userProfileDirectory, "serial": d.Serial}).Fatal("Unable to read content of a folder") + } + + for _, fi := range files { + pf := &DeviceProfile{} + if fi.IsDir() { + continue // Exclude folders if any + } + + // Define a full path of filename + profileLocation := userProfileDirectory + fi.Name() + + // Check if filename has .json extension + if !common.IsValidExtension(profileLocation, ".json") { + continue + } + + fileName := strings.Split(fi.Name(), ".")[0] + if m, _ := regexp.MatchString("^[a-zA-Z0-9-]+$", fileName); !m { + continue + } + + file, err := os.Open(profileLocation) + if err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial, "location": profileLocation}).Warn("Unable to load profile") + continue + } + if err = json.NewDecoder(file).Decode(pf); err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial, "location": profileLocation}).Warn("Unable to decode profile") + continue + } + err = file.Close() + if err != nil { + logger.Log(logger.Fields{"location": profileLocation, "serial": d.Serial}).Warn("Failed to close file handle") + } + + if pf.Serial == d.Serial { + if fileName == d.Serial { + profileList["default"] = pf + } else { + name := strings.Split(fileName, "-")[1] + profileList[name] = pf + } + logger.Log(logger.Fields{"location": profileLocation, "serial": d.Serial}).Info("Loaded custom user profile") + } + } + d.UserProfiles = profileList + d.getDeviceProfile() +} + +// getDeviceProfile will load persistent device configuration +func (d *Device) getDeviceProfile() { + if len(d.UserProfiles) == 0 { + logger.Log(logger.Fields{"serial": d.Serial}).Warn("No profile found for device. Probably initial start") + } else { + for _, pf := range d.UserProfiles { + if pf.Active { + d.DeviceProfile = pf + } + } + } +} + +// setAutoRefresh will refresh device data +func (d *Device) setAutoRefresh() { + timer = time.NewTicker(time.Duration(deviceRefreshInterval) * time.Millisecond) + authRefreshChan = make(chan bool) + go func() { + for { + select { + case <-timer.C: + d.setTemperatures() + case <-authRefreshChan: + timer.Stop() + return + } + } + }() +} + +// setCpuTemperature will store current CPU temperature +func (d *Device) setTemperatures() { + d.CpuTemp = temperatures.GetCpuTemperature() + d.GpuTemp = temperatures.GetGpuTemperature() +} + +// UpdateDeviceLabel will set / update device label +func (d *Device) UpdateDeviceLabel(label string) uint8 { + mutex.Lock() + defer mutex.Unlock() + + d.DeviceProfile.Label = label + d.saveDeviceProfile() + return 1 +} + +// UpdateRgbProfile will update device RGB profile +func (d *Device) UpdateRgbProfile(profile string) uint8 { + if rgb.GetRgbProfile(profile) == nil { + logger.Log(logger.Fields{"serial": d.Serial, "profile": profile}).Warn("Non-existing RGB profile") + return 0 + } + d.DeviceProfile.RGBProfile = profile // Set profile + d.saveDeviceProfile() // Save profile + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() // Restart RGB + return 1 + +} + +// ChangeDeviceBrightness will change device brightness +func (d *Device) ChangeDeviceBrightness(mode uint8) uint8 { + d.DeviceProfile.Brightness = mode + d.saveDeviceProfile() + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() // Restart RGB + return 1 +} + +// ChangeDeviceProfile will change device profile +func (d *Device) ChangeDeviceProfile(profileName string) uint8 { + if profile, ok := d.UserProfiles[profileName]; ok { + currentProfile := d.DeviceProfile + currentProfile.Active = false + d.DeviceProfile = currentProfile + d.saveDeviceProfile() + + // RGB reset + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + + newProfile := profile + newProfile.Active = true + d.DeviceProfile = newProfile + d.saveDeviceProfile() + d.setDeviceColor() + return 1 + } + return 0 +} + +// ChangeKeyboardLayout will change keyboard layout +func (d *Device) ChangeKeyboardLayout(layout string) uint8 { + layouts := keyboards.GetLayouts(keyboardKey) + if len(layouts) < 1 { + return 2 + } + + if slices.Contains(layouts, layout) { + if d.DeviceProfile != nil { + if _, ok := d.DeviceProfile.Keyboards["default"]; ok { + layoutKey := fmt.Sprintf("%s-%s", keyboardKey, layout) + keyboardLayout := keyboards.GetKeyboard(layoutKey) + if keyboardLayout == nil { + logger.Log(logger.Fields{"serial": d.Serial}).Warn("Trying to apply non-existing keyboard layout") + return 2 + } + + d.DeviceProfile.Keyboards["default"] = keyboardLayout + d.DeviceProfile.Layout = layout + d.saveDeviceProfile() + return 1 + } + } else { + logger.Log(logger.Fields{"serial": d.Serial}).Warn("DeviceProfile is null") + return 0 + } + } else { + logger.Log(logger.Fields{"serial": d.Serial}).Warn("No such layout") + return 2 + } + return 0 +} + +// getCurrentKeyboard will return current active keyboard +func (d *Device) getCurrentKeyboard() *keyboards.Keyboard { + if keyboard, ok := d.DeviceProfile.Keyboards[d.DeviceProfile.Profile]; ok { + return keyboard + } + return nil +} + +// SaveKeyboardProfile will save a new keyboard profile +func (d *Device) SaveKeyboardProfile(profileName string, new bool) uint8 { + if new { + if d.DeviceProfile == nil { + return 0 + } + + if slices.Contains(d.DeviceProfile.Profiles, profileName) { + return 2 + } + + if _, ok := d.DeviceProfile.Keyboards[profileName]; ok { + return 2 + } + + d.DeviceProfile.Profiles = append(d.DeviceProfile.Profiles, profileName) + d.DeviceProfile.Keyboards[profileName] = d.getCurrentKeyboard() + d.saveDeviceProfile() + return 1 + } else { + d.saveDeviceProfile() + return 1 + } +} + +// UpdateKeyboardProfile will change keyboard profile +func (d *Device) UpdateKeyboardProfile(profileName string) uint8 { + if d.DeviceProfile == nil { + return 0 + } + + if !slices.Contains(d.DeviceProfile.Profiles, profileName) { + return 2 + } + + if _, ok := d.DeviceProfile.Keyboards[profileName]; !ok { + return 2 + } + + d.DeviceProfile.Profile = profileName + d.saveDeviceProfile() + // RGB reset + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() + return 1 +} + +// DeleteKeyboardProfile will delete keyboard profile +func (d *Device) DeleteKeyboardProfile(profileName string) uint8 { + if d.DeviceProfile == nil { + return 0 + } + + if profileName == "default" { + return 3 + } + + if !slices.Contains(d.DeviceProfile.Profiles, profileName) { + return 2 + } + + if _, ok := d.DeviceProfile.Keyboards[profileName]; !ok { + return 2 + } + + index := common.IndexOfString(d.DeviceProfile.Profiles, profileName) + if index < 0 { + return 0 + } + + d.DeviceProfile.Profile = "default" + d.DeviceProfile.Profiles = append(d.DeviceProfile.Profiles[:index], d.DeviceProfile.Profiles[index+1:]...) + delete(d.DeviceProfile.Keyboards, profileName) + + d.saveDeviceProfile() + // RGB reset + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() + return 1 +} + +// SaveUserProfile will generate a new user profile configuration and save it to a file +func (d *Device) SaveUserProfile(profileName string) uint8 { + if d.DeviceProfile != nil { + profilePath := pwd + "/database/profiles/" + d.Serial + "-" + profileName + ".json" + + newProfile := d.DeviceProfile + newProfile.Path = profilePath + newProfile.Active = false + + buffer, err := json.Marshal(newProfile) + if err != nil { + logger.Log(logger.Fields{"error": err}).Error("Unable to convert to json format") + return 0 + } + + // Create profile filename + file, err := os.Create(profilePath) + if err != nil { + logger.Log(logger.Fields{"error": err, "location": newProfile.Path}).Error("Unable to create new device profile") + return 0 + } + + _, err = file.Write(buffer) + if err != nil { + logger.Log(logger.Fields{"error": err, "location": newProfile.Path}).Error("Unable to write data") + return 0 + } + + err = file.Close() + if err != nil { + logger.Log(logger.Fields{"error": err, "location": newProfile.Path}).Error("Unable to close file handle") + return 0 + } + d.loadDeviceProfiles() + return 1 + } + return 0 +} + +// UpdateDeviceColor will update device color based on selected input +func (d *Device) UpdateDeviceColor(keyId, keyOption int, color rgb.Color) uint8 { + switch keyOption { + case 0: + { + for rowIndex, row := range d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row { + for keyIndex, key := range row.Keys { + if keyIndex == keyId { + key.Color = rgb.Color{ + Red: color.Red, + Green: color.Green, + Blue: color.Blue, + Brightness: 0, + } + d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row[rowIndex].Keys[keyIndex] = key + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() // Restart RGB + return 1 + } + } + } + } + case 1: + { + rowId := -1 + for rowIndex, row := range d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row { + for keyIndex := range row.Keys { + if keyIndex == keyId { + rowId = rowIndex + break + } + } + } + + if rowId < 0 { + return 0 + } + + for keyIndex, key := range d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row[rowId].Keys { + key.Color = rgb.Color{ + Red: color.Red, + Green: color.Green, + Blue: color.Blue, + Brightness: 0, + } + d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row[rowId].Keys[keyIndex] = key + } + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() // Restart RGB + return 1 + } + case 2: + { + for rowIndex, row := range d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row { + for keyIndex, key := range row.Keys { + key.Color = rgb.Color{ + Red: color.Red, + Green: color.Green, + Blue: color.Blue, + Brightness: 0, + } + d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row[rowIndex].Keys[keyIndex] = key + } + } + if d.activeRgb != nil { + d.activeRgb.Exit <- true // Exit current RGB mode + d.activeRgb = nil + } + d.setDeviceColor() // Restart RGB + return 1 + } + } + return 0 +} + +// setDeviceColor will activate and set device RGB +func (d *Device) setDeviceColor() { + // Reset + reset := map[int][]byte{} + var buffer []byte + + // Reset all channels + color := &rgb.Color{ + Red: 0, + Green: 0, + Blue: 0, + Brightness: 0, + } + + for i := 0; i < d.LEDChannels; i++ { + reset[i] = []byte{ + byte(color.Red), + byte(color.Green), + byte(color.Blue), + } + } + + buffer = rgb.SetColor(reset) + d.writeColor(buffer) + + if d.DeviceProfile.RGBProfile == "keyboard" { + var buf = make([]byte, colorPacketLength) + for _, rows := range d.DeviceProfile.Keyboards[d.DeviceProfile.Profile].Row { + for _, keys := range rows.Keys { + for _, packetIndex := range keys.PacketIndex { + buf[packetIndex] = byte(keys.Color.Red) + buf[packetIndex+1] = byte(keys.Color.Green) + buf[packetIndex+2] = byte(keys.Color.Blue) + } + } + } + d.writeColor(buf) // Write color once + return + } + + if d.DeviceProfile.RGBProfile == "static" { + profile := rgb.GetRgbProfile("static") + if d.DeviceProfile.Brightness != 0 { + profile.StartColor.Brightness = rgb.GetBrightnessValue(d.DeviceProfile.Brightness) + } + + profileColor := rgb.ModifyBrightness(profile.StartColor) + for i := 0; i < d.LEDChannels; i++ { + reset[i] = []byte{ + byte(profileColor.Red), + byte(profileColor.Green), + byte(profileColor.Blue), + } + } + buffer = rgb.SetColor(reset) + d.writeColor(buffer) // Write color once + return + } + + go func(lightChannels int) { + lock := sync.Mutex{} + startTime := time.Now() + reverse := false + counterColorpulse := 0 + counterFlickering := 0 + counterColorshift := 0 + counterCircleshift := 0 + counterCircle := 0 + counterColorwarp := 0 + counterSpinner := 0 + counterCpuTemp := 0 + counterGpuTemp := 0 + var temperatureKeys *rgb.Color + colorwarpGeneratedReverse := false + d.activeRgb = rgb.Exit() + + // Generate random colors + d.activeRgb.RGBStartColor = rgb.GenerateRandomColor(1) + d.activeRgb.RGBEndColor = rgb.GenerateRandomColor(1) + + hue := 1 + wavePosition := 0.0 + for { + select { + case <-d.activeRgb.Exit: + return + default: + buff := make([]byte, 0) + + rgbCustomColor := true + profile := rgb.GetRgbProfile(d.DeviceProfile.RGBProfile) + if profile == nil { + for i := 0; i < d.LEDChannels; i++ { + buff = append(buff, []byte{0, 0, 0}...) + } + logger.Log(logger.Fields{"profile": d.DeviceProfile.RGBProfile, "serial": d.Serial}).Warn("No such RGB profile found") + continue + } + rgbModeSpeed := common.FClamp(profile.Speed, 0.1, 10) + // Check if we have custom colors + if (rgb.Color{}) == profile.StartColor || (rgb.Color{}) == profile.EndColor { + rgbCustomColor = false + } + + r := rgb.New( + d.LEDChannels, + rgbModeSpeed, + nil, + nil, + profile.Brightness, + common.Clamp(profile.Smoothness, 1, 100), + time.Duration(rgbModeSpeed)*time.Second, + rgbCustomColor, + ) + + if rgbCustomColor { + r.RGBStartColor = &profile.StartColor + r.RGBEndColor = &profile.EndColor + } else { + r.RGBStartColor = d.activeRgb.RGBStartColor + r.RGBEndColor = d.activeRgb.RGBEndColor + } + + // Brightness + if d.DeviceProfile.Brightness > 0 { + r.RGBBrightness = rgb.GetBrightnessValue(d.DeviceProfile.Brightness) + r.RGBStartColor.Brightness = r.RGBBrightness + r.RGBEndColor.Brightness = r.RGBBrightness + } + + switch d.DeviceProfile.RGBProfile { + case "off": + { + for n := 0; n < d.LEDChannels; n++ { + buff = append(buff, []byte{0, 0, 0}...) + } + } + case "rainbow": + { + r.Rainbow(startTime) + buff = append(buff, r.Output...) + } + case "watercolor": + { + r.Watercolor(startTime) + buff = append(buff, r.Output...) + } + case "cpu-temperature": + { + lock.Lock() + counterCpuTemp++ + if counterCpuTemp >= r.Smoothness { + counterCpuTemp = 0 + } + + if temperatureKeys == nil { + temperatureKeys = r.RGBStartColor + } + + r.MinTemp = profile.MinTemp + r.MaxTemp = profile.MaxTemp + res := r.Temperature(float64(d.CpuTemp), counterCpuTemp, temperatureKeys) + temperatureKeys = res + lock.Unlock() + buff = append(buff, r.Output...) + } + case "gpu-temperature": + { + lock.Lock() + counterGpuTemp++ + if counterGpuTemp >= r.Smoothness { + counterGpuTemp = 0 + } + + if temperatureKeys == nil { + temperatureKeys = r.RGBStartColor + } + + r.MinTemp = profile.MinTemp + r.MaxTemp = profile.MaxTemp + res := r.Temperature(float64(d.GpuTemp), counterGpuTemp, temperatureKeys) + temperatureKeys = res + lock.Unlock() + buff = append(buff, r.Output...) + } + case "colorpulse": + { + lock.Lock() + counterColorpulse++ + if counterColorpulse >= r.Smoothness { + counterColorpulse = 0 + } + + r.Colorpulse(counterColorpulse) + lock.Unlock() + buff = append(buff, r.Output...) + } + case "static": + { + r.Static() + buff = append(buff, r.Output...) + } + case "rotator": + { + r.Rotator(hue) + buff = append(buff, r.Output...) + } + case "wave": + { + r.Wave(wavePosition) + buff = append(buff, r.Output...) + } + case "storm": + { + r.Storm() + buff = append(buff, r.Output...) + } + case "flickering": + { + lock.Lock() + if counterFlickering >= r.Smoothness { + counterFlickering = 0 + } else { + counterFlickering++ + } + + r.Flickering(counterFlickering) + lock.Unlock() + buff = append(buff, r.Output...) + } + case "colorshift": + { + lock.Lock() + if counterColorshift >= r.Smoothness && !reverse { + counterColorshift = 0 + reverse = true + } else if counterColorshift >= r.Smoothness && reverse { + counterColorshift = 0 + reverse = false + } + + r.Colorshift(counterColorshift, reverse) + counterColorshift++ + lock.Unlock() + buff = append(buff, r.Output...) + } + case "circleshift": + { + lock.Lock() + if counterCircleshift >= lightChannels { + counterCircleshift = 0 + } else { + counterCircleshift++ + } + + r.Circle(counterCircleshift) + lock.Unlock() + buff = append(buff, r.Output...) + } + case "circle": + { + lock.Lock() + if counterCircle >= lightChannels { + counterCircle = 0 + } else { + counterCircle++ + } + + r.Circle(counterCircle) + lock.Unlock() + buff = append(buff, r.Output...) + } + case "spinner": + { + lock.Lock() + if counterSpinner >= lightChannels { + counterSpinner = 0 + } else { + counterSpinner++ + } + r.Spinner(counterSpinner) + lock.Unlock() + buff = append(buff, r.Output...) + } + case "colorwarp": + { + lock.Lock() + if counterColorwarp >= r.Smoothness { + if !colorwarpGeneratedReverse { + colorwarpGeneratedReverse = true + d.activeRgb.RGBStartColor = d.activeRgb.RGBEndColor + d.activeRgb.RGBEndColor = rgb.GenerateRandomColor(r.RGBBrightness) + } + counterColorwarp = 0 + } else if counterColorwarp == 0 && colorwarpGeneratedReverse == true { + colorwarpGeneratedReverse = false + } else { + counterColorwarp++ + } + + r.Colorwarp(counterColorwarp, d.activeRgb.RGBStartColor, d.activeRgb.RGBEndColor) + lock.Unlock() + buff = append(buff, r.Output...) + } + } + // Send it + d.writeColor(buff) + time.Sleep(20 * time.Millisecond) + hue++ + wavePosition += 0.2 + } + } + }(d.LEDChannels) +} + +// writeColor will write data to the device with a specific endpoint. +// writeColor does not require endpoint closing and opening like normal Write requires. +// Endpoint is open only once. Once the endpoint is open, color can be sent continuously. +func (d *Device) writeColor(data []byte) { + buffer := make([]byte, len(dataTypeSetColor)+len(data)+headerWriteSize) + binary.LittleEndian.PutUint16(buffer[0:2], uint16(len(data))) + copy(buffer[headerWriteSize:headerWriteSize+len(dataTypeSetColor)], dataTypeSetColor) + copy(buffer[headerWriteSize+len(dataTypeSetColor):], data) + + // Split packet into chunks + chunks := common.ProcessMultiChunkPacket(buffer, maxBufferSizePerRequest) + for i, chunk := range chunks { + if i == 0 { + // Initial packet is using cmdWriteColor + _, err := d.transfer(cmdWriteColor, chunk) + if err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial}).Error("Unable to write to color endpoint") + } + } else { + // Chunks don't use cmdWriteColor, they use static dataTypeSubColor + _, err := d.transfer(dataTypeSubColor, chunk) + if err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial}).Error("Unable to write to endpoint") + } + } + } +} + +// transfer will send data to a device and retrieve device output +func (d *Device) transfer(endpoint, buffer []byte) ([]byte, error) { + // Packet control, mandatory for this device + mutex.Lock() + defer mutex.Unlock() + + // Create write buffer + bufferW := make([]byte, bufferSizeWrite) + bufferW[1] = 0x08 + endpointHeaderPosition := bufferW[headerSize : headerSize+len(endpoint)] + copy(endpointHeaderPosition, endpoint) + if len(buffer) > 0 { + copy(bufferW[headerSize+len(endpoint):headerSize+len(endpoint)+len(buffer)], buffer) + } + + // Create read buffer + bufferR := make([]byte, bufferSize) + + // Send command to a device + if _, err := d.dev.Write(bufferW); err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial}).Error("Unable to write to a device") + return nil, err + } + + // Get data from a device + if _, err := d.dev.Read(bufferR); err != nil { + logger.Log(logger.Fields{"error": err, "serial": d.Serial}).Error("Unable to read data from device") + return nil, err + } + + return bufferR, nil +} diff --git a/src/keyboards/keyboards.go b/src/keyboards/keyboards.go index e3905cc..6bbc219 100644 --- a/src/keyboards/keyboards.go +++ b/src/keyboards/keyboards.go @@ -42,6 +42,7 @@ type Key struct { PacketIndex []int `json:"packetIndex"` Color rgb.Color `json:"color"` Zone int `json:"zone"` + Svg bool `json:"svg"` } // Init will load and initialize keyboard data diff --git a/src/server/requests/requests.go b/src/server/requests/requests.go index 27ea2cb..4f5444a 100755 --- a/src/server/requests/requests.go +++ b/src/server/requests/requests.go @@ -520,7 +520,7 @@ func ProcessChangeKeyboardLayout(r *http.Request) *Payload { } } - if len(req.KeyboardLayout) < 0 { + if len(req.KeyboardLayout) < 1 { return &Payload{Message: "Invalid profile name", Code: http.StatusOK, Status: 0} } diff --git a/src/templates/templates.go b/src/templates/templates.go index df170b4..e3970a3 100644 --- a/src/templates/templates.go +++ b/src/templates/templates.go @@ -48,6 +48,7 @@ func Init() { "web/memory.html", "web/k65pm.html", "web/k70core.html", + "web/k70pro.html", "web/k55core.html", "web/rgb.html", "web/temperature.html", diff --git a/static/img/icons/icon-backward.svg b/static/img/icons/icon-backward.svg new file mode 100644 index 0000000..1c8c071 --- /dev/null +++ b/static/img/icons/icon-backward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-brightness.svg b/static/img/icons/icon-brightness.svg new file mode 100644 index 0000000..6ca4bed --- /dev/null +++ b/static/img/icons/icon-brightness.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-forward.svg b/static/img/icons/icon-forward.svg new file mode 100644 index 0000000..72d299e --- /dev/null +++ b/static/img/icons/icon-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-lock.svg b/static/img/icons/icon-lock.svg new file mode 100644 index 0000000..b468e33 --- /dev/null +++ b/static/img/icons/icon-lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-mute.svg b/static/img/icons/icon-mute.svg new file mode 100644 index 0000000..d348e1a --- /dev/null +++ b/static/img/icons/icon-mute.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-pause.svg b/static/img/icons/icon-pause.svg new file mode 100644 index 0000000..691c3a2 --- /dev/null +++ b/static/img/icons/icon-pause.svg @@ -0,0 +1 @@ +play-pause \ No newline at end of file diff --git a/static/img/icons/icon-stop.svg b/static/img/icons/icon-stop.svg new file mode 100644 index 0000000..6c74446 --- /dev/null +++ b/static/img/icons/icon-stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/icons/icon-user.svg b/static/img/icons/icon-user.svg new file mode 100644 index 0000000..421be5c --- /dev/null +++ b/static/img/icons/icon-user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/devices.html b/web/devices.html index c9bce2d..a145298 100644 --- a/web/devices.html +++ b/web/devices.html @@ -22,4 +22,6 @@ {{ template "k70core.html" . }} {{ else if eq .Device.Template "k55core.html" }} {{ template "k55core.html" . }} +{{ else if eq .Device.Template "k70pro.html" }} +{{ template "k70pro.html" . }} {{ end }} \ No newline at end of file diff --git a/web/k70pro.html b/web/k70pro.html new file mode 100644 index 0000000..95955db --- /dev/null +++ b/web/k70pro.html @@ -0,0 +1,258 @@ + + + + + + {{.Title}} + + + + + + + + + + + + +
+
+ {{ $devs := .Devices }} + {{ $temperatures := .Temperatures }} + {{ $device := .Device }} + {{ $rgb := .Rgb }} + {{ $profile := $device.DeviceProfile.Profile }} + {{ $keyboard := index $device.DeviceProfile.Keyboards $profile }} + + + +
+
+
+
+
+
+
+ Device +
+
+ {{ .Device.Product }}
+

+ Firmware: {{ .Device.Firmware }} +

+
+
+
+ + + + + + + + + + + + + + + + +

Layout

User Profile

Brightness

RGB Profile

Save Profile

+ + + + + + + + + +
+
+ {{ if eq "keyboard" $device.DeviceProfile.RGBProfile }} +
+ {{ range $index, $keys := $keyboard.Row }} + {{ if eq $index 4 }} +
+ {{ else if eq $index 6 }} +
+ {{ else }} +
+ {{ end }} + {{ range $index, $keys := .Keys }} +
+

+ {{ if $keys.Svg }} + Icon + {{ else }} + {{ $keys.KeyName }} + {{ end }} +

+
+ {{ end }} +
+ {{ end }} +
+
+
+
+
+ + + + + + + +
+
+ + + + +
+
+ {{ end }} +
+
+
+
+
+ +
+
+ + + + + + \ No newline at end of file