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 @@
+
\ 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 }}
+
+
+
+
+
+ {{ range $d := $devs }}
+ {{ if eq $d.Serial $device.Serial }}
+
+ {{ else }}
+
+ {{ end }}
+ {{ end }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 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 }}
+
+ {{ else }}
+ {{ $keys.KeyName }}
+ {{ end }}
+
+
+ {{ end }}
+
+ {{ end }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ end }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file