diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c948f71
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+bin/*
+lib/*
+.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..5ef5d45
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,19 @@
+# g810-led CONTRIBUTING
+
+## Share your profile or make dump :
+* [https://github.com/MatMoul/g810-led-resources](https://github.com/MatMoul/g810-led-resources)
+
+## Contributing :
+* Open an issue before work to hard
+* Work on the develop branch
+
+## Tasks (Help wanted) :
+* (0%)  AVERAGE:     Add selection of multiple keyboards by index
+* (0%)  AVERAGE:     Improve INSTALL.md to support more distributions (And CentOS with hidapi)
+* (10%) AVERAGE:     Make distributions ready package
+* (0%)  IMAGINATION: Use a more generic name for project, config dir and systemd units ([issue 29](https://github.com/MatMoul/g810-led/issues/29))
+
+## Possible enhancements :
+* Add deamon mode for custom effects (Add G Keys handler [issue 39](https://github.com/MatMoul/g810-led/issues/39), [CReimer](https://github.com/CReimer/g910-gkey-uinput)))
+* Support country based keybord ([issue 16](https://github.com/MatMoul/g810-led/issues/16) but very hard)
+* Support of new G Mouse like the G403 ([issue 40](https://github.com/MatMoul/g810-led/issues/40))
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
new file mode 100644
index 0000000..93320bf
--- /dev/null
+++ b/CONTRIBUTORS.md
@@ -0,0 +1,55 @@
+# g810-led - CONTRIBUTORS (alpha order) :
+
+## [andreast1990](https://github.com/andreast1990) :
+* Add Wireshark dump for g910
+
+## [barul42](https://github.com/barul42) :
+* Improve INSTALL.MD
+
+## [carlba](https://github.com/carlba) :
+* Improve install file
+
+## [CReimer](https://github.com/CReimer) :
+* Add Wireshark dump for g910 M and G keys
+* Debug hidapi missing keys on certain computer
+
+## [dkolosa](https://github.com/dkolosa) :
+* Provide alternative productid for g910
+
+## [francoisfreitag](https://github.com/francoisfreitag) :
+* Refactor makefile (hard work)
+
+## [hschreck](https://github.com/hschreck) :
+* Fix typo
+
+## [jdagerbo](https://github.com/jdagerbo) :
+* Refactor many of the code (very hard work)
+
+## [Landrovan](https://github.com/Landrovan) :
+* Improve support of G410 (two times)
+
+## [larsnaesbye](https://github.com/larsnaesbye) :
+* Fix typo
+
+## [lynix](https://github.com/lynix) :
+* Improve makefile
+
+## [matthunz](https://github.com/matthunz) :
+* Improve INSTALL.MD for ArchLinux
+
+## [MohamadSaada](https://github.com/MohamadSaada) :
+* Add poweron effect bytes for g910
+* Debug setKeys (hard work)
+
+## [noisycat](https://github.com/noisycat) :
+* Add wireshark dump effects
+* Improve makefile
+
+## [pearsonk](https://github.com/pearsonk) :
+* Add g213 protocol (very hard work)
+* Add support of g213
+* Implement underlying device IO as a shared library
+* Add multiple keyboard support (very hard work)
+
+## [wextia](https://github.com/wextia) :
+* Fixed incorrect markdown formatting in README.md
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..02c9786
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,59 @@
+# Installation :
+
+## Build dependencies :
+* git
+* g++
+* make
+
+## Dependencies :
+* hidapi or libusb
+
+## hidapi vs libusb :
+hidapi is a newer implementation but needs more testing.
+hidapi is more responsive than libusb (~20ms vs ~150ms).
+hidapi seems to not work on CentOS, writing to hidraw is not allowed.
+hidapi is recommended but if you encounter a problem on your system, switch to libusb.
+
+
+## Installation using repos :
+ArchLinux (aur) :
+`yaourt -S g810-led-git` # with yaourt
+`pacaur -S g810-led-git` # with pacaur
+
+
+## Installation of dependencies :
+ArchLinux :
+`sudo pacman -S git gcc make hidapi` # for hidapi
+`sudo pacman -S git gcc make libusb` # for libusb
+Debian :
+`sudo apt-get install git g++ make libhidapi-dev` # for hidapi
+`sudo apt-get install git g++ make libusb-1.0-0-dev` # for libusb
+Fedora :
+`sudo dnf install git make gcc-c++ hidapi-devel` # for hidapi
+`sudo dnf install git make gcc-c++ libusbx-devel` # for libusb
+
+## Installation :
+`git clone https://github.com/MatMoul/g810-led.git`
+`cd g810-led`
+`make bin` # for hidapi
+`make bin LIB=libusb` # for libusb
+`sudo make install`
+
+## Installation of the library (For developers) :
+`make lib` # for hidapi
+`make lib LIB=libusb` # for libusb
+`sudo make install-lib` to install the libg810-led library.
+`sudo make install-dev` to install the libg810-led library and headers for development.
+
+## Update :
+Same as install, but your profile and reboot files are preserved.
+
+## Uninstall :
+`sudo make uninstall`
+
+## Boot profiles :
+On boot, the keyboard is set with the udev file /etc/udev/rules.d/g810-led.rules
+This file launches the profile stored in /etc/g810-led/profile
+To prevent your keyboard flashing 3 times when you reboot use the systemd unit (g810-led-reboot).
+
+Samples can be found in /etc/g810-led/samples.
diff --git a/README.md b/README.md
index cf1e9e3..9d653d4 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,55 @@
-# g810-led
+# g810-led
 
-Linux LED controller for the Logitech G810 Orion Spectrum Keyboard
+Linux led controller for Logitech G213, G410, G413, G512, G513, G610, G810, G910 and GPRO Keyboards.
 
-Other compatible keyboard :
-G410 Atlas Spectrum
-G610 Orion
-G910 Orion Spark
-G910 Orion Spectrum
+## Compatible keyboards :
+- **G213 Prodigy**
+- **G410 Atlas Spectrum**
+- **G413 Carbon**
+- **G512 Carbon**
+- **G513 Carbon**
+- **G610 Orion Brown**
+- **G610 Orion Red**
+- **G810 Orion Spectrum**
+- **G910 Orion Spark**
+- **G910 Orion Spectrum**
+- **GPRO**
 
-
+## Contribute and evolution :
+* [CONTRIBUTING.md](https://github.com/MatMoul/g810-led/blob/master/CONTRIBUTING.md)
 
-Install and use :
-- look at the wiki : https://github.com/MatMoul/g810-led/wiki
+## Install :
+* [INSTALL.md](https://github.com/MatMoul/g810-led/blob/master/INSTALL.md)
 
-Samples :
-`g810-led -p /etc/g810/profile # Set a profile`
+## Help :
+`g213-led --help`
+`g410-led --help`
+`g413-led --help`
+`g512-led --help`
+`g513-led --help`
+`g610-led --help`
+`g810-led --help`
+`g910-led --help`
+`gpro-led --help`
+
+`g810-led --help-keys`
+`g810-led --help-effects`
+`g810-led --help-samples`
+
+## Samples :
+`g810-led -p /etc/g810/profile # Load a profile`
 `g810-led -k logo ff0000 # Set color of a key`
 `g810-led -a 00ff00 # Set color of all keys`
 `g810-led -g fkeys ff00ff # Set color of a group of keys`
 `g810-led -s color # Set keyboard power on effect`
+`g810-led -fx color keys 00ff00 # Set fixed color effect`
+`g810-led -fx breathing logo 00ff00 0a # Set breathing effect`
+`g810-led -fx cycle all 0a # Set color cycle effect`
+`g810-led -fx hwave keys 0a # Set horizontal wave effect`
+`g810-led -fx vwave keys 0a # Set vertical wave effect`
+`g810-led -fx cwave keys 0a # Set center wave effect`
 
-Samples with no commit :
+## Samples with no commit :
 `g810-led -an 000000 # Set color of all key with no action`
 `g810-led -gn modifiers ff0000 # Set color of a group with no action`
 `g810-led -kn w ff0000 # Set color of a key with no action`
@@ -28,3 +57,43 @@ Samples with no commit :
 `g810-led -kn s ff0000 # Set color of a key with no action`
 `g810-led -kn d ff0000 # Set color of a key with no action`
 `g810-led -c # Commit all changes`
+
+## Samples for G610 :
+`g610-led -a 60 # Set intensity of all keys`
+`g610-led -k logo ff # Set intensity of a key`
+`g610-led -g fkeys aa # Set intensity of a group of keys`
+
+## Samples for G213 :
+`g213-led -a 00ff00 # Set all keys green`
+`g213-led -r 1 ff0000 # Set region 1 red`
+
+## Samples with pipe (for effects) :
+`g810-led -pp < profilefile # Load a profile`
+`echo -e "k w ff0000\nk a ff0000\nk s ff0000\nk d ff0000\nc" | g810-led -pp # Set multiple keys`
+
+## Testing unsuported keyboards :
+Start by retrieving the VendorID and the ProductID of your keyboard using lsusb.
+`lsusb`
+Sample return :
+`Bus 001 Device 001: ID 046d:c331 Logitech, Inc.`
+In this sample VendorID is 046d and ProductID is c331. Now test your keyboard with all supported protocol :
+`g810-led -dv 046d -dp c331 -tuk 1 -a 000000`
+If your keyboard set all key to off you have found the protocol (1), if not continue.
+`g810-led -dv 046d -dp c331 -tuk 2 -a 000000`
+If your keyboard set all key to off you have found the protocol (2), if not continue.
+`g810-led -dv 046d -dp c331 -tuk 3 -a 000000`
+If your keyboard set all key to off you have found the protocol (3), if not, need new dump.
+
+## Building and linking against the libg810-led library :
+Include in implementing source files.
+```cpp
+#include 
+```
+To link, simply provide `-lg810-led` to the build flags.
+
+To build the g810-led application as a dynamically-linked variant, run the target:
+`make bin-linked`
+
+## Dumps :
+Dumps of keyboards are now stored in a separate project to preserve a small download size of this project.
+You can find them here : [https://github.com/MatMoul/g810-led-resources](https://github.com/MatMoul/g810-led-resources)
diff --git a/makefile b/makefile
index 24826e8..3741c14 100644
--- a/makefile
+++ b/makefile
@@ -1,20 +1,120 @@
-CC=g++
-CFLAGS=-Wall -O2 -std=gnu++11
-LIBUSB_INC?=-I/usr/include/libusb-1.0
-LDFLAGS=-lusb-1.0
+CXX?=g++
+CXXFLAGS?=-Wall -O2
+LIB?=hidapi
+ifeq ($(LIB),libusb)
+	CPPFLAGS=-Dlibusb
+	LIBS=-lusb-1.0
+else
+	CPPFLAGS=-Dhidapi
+	LIBS=-lhidapi-hidraw
+endif
+SYSTEMDDIR?=/usr/lib/systemd
+
+PREFIX?=$(DESTDIR)/usr
+libdir?=$(PREFIX)/lib
+includedir?=$(PREFIX)/include
+
+# Program & versioning information
 PROGN=g810-led
+MAJOR=0
+MINOR=2
+MICRO=8
 
-.PHONY: all debug clean
+CXXFLAGS+=-std=gnu++11 -DVERSION=\"$(MAJOR).$(MINOR).$(MICRO)\"
+APPSRCS=src/main.cpp src/helpers/*.cpp src/helpers/*.h
+LIBSRCS=src/classes/*.cpp src/classes/*.h
 
-all: bin/$(PROGN)
+.PHONY: all bin debug clean setup install uninstall lib install-lib install-dev
 
-bin/$(PROGN): src/main.cpp src/classes/*.cpp src/classes/*.h
+all: lib/lib$(PROGN).so bin/$(PROGN)
+
+bin: bin/$(PROGN)
+
+bin/$(PROGN): $(APPSRCS) $(LIBSRCS)
 	@mkdir -p bin
-	$(CC) $(CFLAGS) $(LIBUSB_INC) -o $@ $^ $(LDFLAGS)
-
-debug: CFLAGS += -g -Wextra -pedantic
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+	
+debug: CXXFLAGS += -g -Wextra -pedantic
 debug: bin/$(PROGN)
 
-clean:
-	rm -rf bin
+lib/lib$(PROGN).so: $(LIBSRCS)
+	@mkdir -p lib
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -fPIC -shared -Wl,-soname,lib$(PROGN).so -o lib/lib$(PROGN).so.$(MAJOR).$(MINOR).$(MICRO) $^ $(LIBS)
+	@ln -sf lib$(PROGN).so.$(MAJOR).$(MINOR).$(MICRO) lib/lib$(PROGN).so
 
+bin-linked: lib/lib$(PROGN).so
+	@mkdir -p bin
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(APPSRCS) -o bin/$(PROGN) $(LIBS) -L./lib -l$(PROGN)
+
+lib: lib/lib$(PROGN).so
+
+clean:
+	@rm -rf bin
+	@rm -rf lib
+
+setup:
+	@install -m 755 -d \
+		$(DESTDIR)/usr/bin \
+		$(DESTDIR)/etc/$(PROGN)/samples \
+		$(DESTDIR)/etc/udev/rules.d
+	@cp bin/$(PROGN) $(DESTDIR)/usr/bin
+	@test -s $(DESTDIR)/usr/bin/g213-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g213-led
+	@test -s $(DESTDIR)/usr/bin/g410-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g410-led
+	@test -s $(DESTDIR)/usr/bin/g413-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g413-led
+	@test -s $(DESTDIR)/usr/bin/g512-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g512-led
+	@test -s $(DESTDIR)/usr/bin/g513-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g513-led
+	@test -s $(DESTDIR)/usr/bin/g610-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g610-led
+	@test -s $(DESTDIR)/usr/bin/g910-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/g910-led
+	@test -s $(DESTDIR)/usr/bin/gpro-led || ln -s /usr/bin/$(PROGN) $(DESTDIR)/usr/bin/gpro-led
+	@cp sample_profiles/* $(DESTDIR)/etc/$(PROGN)/samples
+	@cp udev/$(PROGN).rules $(DESTDIR)/etc/udev/rules.d
+	@test -s /usr/bin/systemd-run && \
+		install -m 755 -d $(DESTDIR)$(SYSTEMDDIR)/system && \
+		cp systemd/$(PROGN)-reboot.service $(DESTDIR)$(SYSTEMDDIR)/system
+
+install-lib: lib
+	@install -m 755 -d $(libdir)
+	@install -m 644 lib/lib$(PROGN).so.$(MAJOR).$(MINOR).$(MICRO) $(libdir)/
+	@ln -sf lib$(PROGN).so.$(MAJOR).$(MINOR).$(MICRO) $(libdir)/lib$(PROGN).so
+
+install-dev: install-lib
+	@mkdir -p $(includedir)/$(PROGN)/
+	@install -m 644 src/classes/*.h $(includedir)/$(PROGN)
+
+install: setup
+	@test -s /etc/$(PROGN)/profile || \
+		cp /etc/$(PROGN)/samples/group_keys /etc/$(PROGN)/profile
+	@test -s /etc/$(PROGN)/reboot || \
+		cp /etc/$(PROGN)/samples/all_off /etc/$(PROGN)/reboot
+	@udevadm control --reload-rules
+	@$(PROGN) -p /etc/$(PROGN)/profile
+	@test -s /usr/bin/systemd-run && \
+		systemctl daemon-reload && \
+		systemctl enable $(PROGN)-reboot
+
+uninstall-lib:
+	@rm -f $(libdir)/lib$(PROGN).so*
+
+uninstall-dev:
+	@rm -rf $(includedir)/$(PROGN)
+
+uninstall:
+	@test -s /usr/bin/systemd-run && \
+		systemctl disable $(PROGN)-reboot && \
+		rm $(SYSTEMDDIR)/system/$(PROGN)-reboot.service && \
+		systemctl daemon-reload && \
+		rm -R /etc/$(PROGN)
+	
+	@rm /usr/bin/g213-led
+	@rm /usr/bin/g410-led
+	@rm /usr/bin/g413-led
+	@rm /usr/bin/g413-led
+	@rm /usr/bin/g512-led
+	@rm /usr/bin/g513-led
+	@rm /usr/bin/g610-led
+	@rm /usr/bin/g910-led
+	@rm /usr/bin/gpro-led
+	@rm /usr/bin/$(PROGN)
+	
+	@rm /etc/udev/rules.d/$(PROGN).rules
+	@udevadm control --reload-rules
diff --git a/makerelease b/makerelease
new file mode 100755
index 0000000..148e1bf
--- /dev/null
+++ b/makerelease
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+if [ "$1" == "" ]; then
+	echo "Error: No version provided"
+	echo "./makerelease 0.0.1"
+	exit 1
+fi
+
+ssh -T git@github.com
+if [ ! "$?" = "1" ]; then
+  echo "No Github ssh key loaded exiting..."
+  exit 1
+fi
+
+clear
+branch=$(git rev-parse --abbrev-ref HEAD)
+read -p "Current branch is $branch. Continue ? (y/N)" choice
+case "$choice" in 
+  n|N|'' )
+    echo "Cancel !"
+    exit 1
+  ;;
+  y|Y ) echo "Make release...";;
+  * )
+    echo "Cancel !"
+    exit 1
+  ;;
+esac
+
+
+
+# Ready to update :
+
+version=$1
+
+sed -i "/string version = /c\\\tstring version = \"$version\";" src/helpers/help.cpp
+IFS='.' read -ra VPART <<< "$version"
+sed -i "/MAJOR=/cMAJOR=${VPART[0]}" makefile
+sed -i "/MINOR=/cMINOR=${VPART[1]}" makefile
+sed -i "/MICRO=/cMICRO=${VPART[2]}" makefile
+
+git commit -m "Version $version" makefile src/*
+git push
+
+git checkout master
+git merge develop
+git push
+
+git tag -a "v$version" -m "Version $version"
+git push --tags
+
+git checkout $branch
+
+#wget https://github.com/MatMoul/g810-led/archive/v$version.zip
+#wget https://github.com/MatMoul/g810-led/archive/v$version.tar.gz
+
+#Publish to aur...
diff --git a/pictures/logitech_g810-2.jpg b/pictures/logitech_g810-2.jpg
deleted file mode 100644
index 0cb5d34..0000000
Binary files a/pictures/logitech_g810-2.jpg and /dev/null differ
diff --git a/sample_effects/bash/k2000 b/sample_effects/bash/k2000
new file mode 100755
index 0000000..c5c85d5
--- /dev/null
+++ b/sample_effects/bash/k2000
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+if [ "$1" == "--help" ]; then
+	echo "k2000 [speed (default:0.01] [colorOff] [colorOn] [colorFade1] [colorFade2]"
+	exit 0
+fi
+
+speed=0.01
+colorOff=000000
+colorOn=ff0000
+colorFade1=aa0000
+colorFade2=550000
+
+if [ "$1" != "" ]; then
+	speed=$1
+fi
+if [ "$2" != "" ]; then
+	colorOff=$2
+fi
+if [ "$3" != "" ]; then
+	colorOn=$3
+	if [ "$4" != "" ]; then
+		colorFade1=$4
+	else
+		colorFade1=$colorOn
+	fi
+	if [ "$5" != "" ]; then
+		colorFade2=$5
+	else
+		colorFade2=$colorOn
+	fi
+fi
+
+setKeys () {
+	cmd=""
+  if [ $1 ] ; then
+    cmd=$cmd"k $1 $colorOn\n"
+  fi
+  if [ $2 ] ; then
+    cmd=$cmd"k $2 $colorFade1\n"
+  fi
+  if [ $3 ] ; then
+    cmd=$cmd"k $3 $colorFade2\n"
+  fi
+  if [ $4 ] ; then
+    cmd=$cmd"k $4 $colorOff\n"
+  fi
+  cmd=$cmd"c"
+  echo -e $cmd | g810-led -pp
+  sleep $speed
+}
+
+g810-led -gn fkeys $colorOff
+
+setKeys F1
+setKeys F2
+setKeys F3
+setKeys F4
+setKeys F5 F1
+setKeys F6 F2 F1
+setKeys F7 F3 F2 F1
+setKeys F8 F4 F3 F2
+setKeys F9 F5 F4 F3
+setKeys F10 F6 F5 F4
+setKeys F11 F7 F6 F5
+setKeys F12 F8 F7 F6
+setKeys F12 F9 F8 F7
+setKeys F12 F10 F9 F8
+setKeys F12 F11 F10 F9
+setKeys F11 F10 F10 F10
+setKeys F10
+setKeys F9
+setKeys F8 F12
+setKeys F7 F11 F12
+setKeys F6 F10 F11 F12
+setKeys F5 F9 F10 F11
+setKeys F4 F8 F9 F10
+setKeys F3 F7 F8 F9
+setKeys F2 F6 F7 F8
+setKeys F1 F5 F6 F7
+setKeys F1 F4 F5 F6
+setKeys F1 F3 F4 F5
+setKeys F1 F2 F3 F4
+setKeys F1 F1 F2 F3
+setKeys F1 F1 F1 F2
+setKeys F1 F1 F1 F1
diff --git a/sample_effects/python/k2000 b/sample_effects/python/k2000
new file mode 100755
index 0000000..ce3c031
--- /dev/null
+++ b/sample_effects/python/k2000
@@ -0,0 +1,96 @@
+#!/usr/bin/python2
+
+import sys
+import subprocess
+import time
+
+
+if len(sys.argv) > 1:
+	if sys.argv[1] == '--help':
+		print 'k2000 [speed (default:0.01] [colorOff] [colorOn] [colorFade1] [colorFade2]'
+		sys.exit()
+
+
+speed = 0.01
+colorOff = '000000'
+colorOn = 'ff0000'
+colorFade1 = 'aa0000'
+colorFade2 = '550000'
+
+if len(sys.argv) > 1:
+	try:
+		speed = float(sys.argv[1])
+	except:
+		print 'Speed arg error'
+if len(sys.argv) > 2:
+	try:
+		colorOff = sys.argv[2]
+	except:
+		print 'colorOff arg error'
+if len(sys.argv) > 3:
+	try:
+		colorOn = sys.argv[3]
+	except:
+		print 'colorOn arg error'
+if len(sys.argv) > 4:
+	try:
+		colorFade1 = sys.argv[4]
+	except:
+		print 'colorFade1 arg error'
+if len(sys.argv) > 5:
+	try:
+		colorFade2 = sys.argv[5]
+	except:
+		print 'colorFade2 arg error'
+
+
+def setKeys(keys):
+	pipeValue = ''
+	for index in range(len(keys)):
+		if index == 0:
+			pipeValue = pipeValue + 'k ' + keys[index] + ' ' + colorOn + '\\n'
+		elif index == 1:
+			pipeValue = pipeValue + 'k ' + keys[index] + ' ' + colorFade1 + '\\n'
+		elif index == 2:
+			pipeValue = pipeValue + 'k ' + keys[index] + ' ' + colorFade2 + '\\n'
+		elif index == 3:
+			pipeValue = pipeValue + 'k ' + keys[index] + ' ' + colorOff + '\\n'
+	pipeValue = pipeValue + 'c'
+	subprocess.call('echo -e "' + pipeValue + '" | g810-led -pp', shell=True)
+	time.sleep(speed)
+
+
+subprocess.call('g810-led -gn fkeys ' + colorOff, shell=True)
+
+setKeys(['F1'])
+setKeys(['F2'])
+setKeys(['F3'])
+setKeys(['F4'])
+setKeys(['F5','F1'])
+setKeys(['F6','F2','F1'])
+setKeys(['F7','F3','F2','F1'])
+setKeys(['F8','F4','F3','F2'])
+setKeys(['F9','F5','F4','F3'])
+setKeys(['F10','F6','F5','F4'])
+setKeys(['F11','F7','F6','F5'])
+setKeys(['F12','F8','F7','F6'])
+setKeys(['F12','F9','F8','F7'])
+setKeys(['F12','F10','F9','F8'])
+setKeys(['F12','F11','F10','F9'])
+setKeys(['F11','F10','F10','F10'])
+setKeys(['F10'])
+setKeys(['F9'])
+setKeys(['F8','F12'])
+setKeys(['F7','F11','F12'])
+setKeys(['F6','F10','F11','F12'])
+setKeys(['F5','F9','F10','F11'])
+setKeys(['F4','F8','F9','F10'])
+setKeys(['F3','F7','F8','F9'])
+setKeys(['F2','F6','F7','F8'])
+setKeys(['F1','F5','F6','F7'])
+setKeys(['F1','F4','F5','F6'])
+setKeys(['F1','F3','F4','F5'])
+setKeys(['F1','F2','F3','F4'])
+setKeys(['F1','F1','F2','F3'])
+setKeys(['F1','F1','F1','F2'])
+setKeys(['F1','F1','F1','F1'])
diff --git a/sample_profiles/all_blue_fxl_breathing_red b/sample_profiles/all_blue_fxl_breathing_red
new file mode 100644
index 0000000..8e8ccf4
--- /dev/null
+++ b/sample_profiles/all_blue_fxl_breathing_red
@@ -0,0 +1,5 @@
+a 0000ff # Set all keys blue
+
+c # Commit changes
+
+fx breathing logo ff0000 10 # Set breathing effect with red color and speed 10
diff --git a/sample_profiles/colors b/sample_profiles/colors
new file mode 100644
index 0000000..d87c950
--- /dev/null
+++ b/sample_profiles/colors
@@ -0,0 +1,28 @@
+# Sample profile using Groups of Keys and Single Characters.
+
+g logo 000096       # Blue
+g indicators ffffff # White
+g multimedia 009600 # Green
+g fkeys ff7700      # Orange
+g modifiers ff7700  # Orange
+g arrows 0000ff     # Red
+g numeric 00ff00    # Green
+g functions ffffff  # White
+g keys ff00ff       # Purple
+g gkeys ffffff      # White
+k tilde ff0000      # Red
+k W 0000ff          # Blue {
+k A 0000ff
+k S 0000ff
+k D 0000ff          # }         
+k 1 ff0000          # Red {
+k 2 ff0000
+k 3 ff0000
+k 4 ff0000
+k 5 ff0000
+k 6 ff0000
+k 7 ff0000
+k 8 ff0000
+k 9 ff0000
+k 0 ff0000          # }
+c                   # Commit changes
diff --git a/sample_profiles/fx_breathing_red b/sample_profiles/fx_breathing_red
new file mode 100644
index 0000000..250349c
--- /dev/null
+++ b/sample_profiles/fx_breathing_red
@@ -0,0 +1,2 @@
+
+fx breathing all ff0000 20 # Set breathing effect with red color and speed 20
diff --git a/sample_profiles/fx_color_green b/sample_profiles/fx_color_green
new file mode 100644
index 0000000..97264a7
--- /dev/null
+++ b/sample_profiles/fx_color_green
@@ -0,0 +1,3 @@
+# Green Profile
+
+fx color all 00ff00 # Set all keys green
diff --git a/sample_profiles/fx_cwave b/sample_profiles/fx_cwave
new file mode 100644
index 0000000..347a1bc
--- /dev/null
+++ b/sample_profiles/fx_cwave
@@ -0,0 +1,2 @@
+
+fx cwave all 20 # Set center wave effect with speed 20
diff --git a/sample_profiles/fx_cycle b/sample_profiles/fx_cycle
new file mode 100644
index 0000000..249adcb
--- /dev/null
+++ b/sample_profiles/fx_cycle
@@ -0,0 +1,2 @@
+
+fx cycle all 20 # Set color cycle effect with speed 20
diff --git a/sample_profiles/fx_hwave b/sample_profiles/fx_hwave
new file mode 100644
index 0000000..cea0f8a
--- /dev/null
+++ b/sample_profiles/fx_hwave
@@ -0,0 +1,2 @@
+
+fx hwave all 20 # Set horizontal wave effect with speed 20
diff --git a/sample_profiles/fx_vwave b/sample_profiles/fx_vwave
new file mode 100644
index 0000000..a52aca2
--- /dev/null
+++ b/sample_profiles/fx_vwave
@@ -0,0 +1,2 @@
+
+fx vwave all 20 # Set vertical wave effect with speed 20
diff --git a/sample_profiles/group_keys b/sample_profiles/group_keys
index 98afb1a..53c92df 100644
--- a/sample_profiles/group_keys
+++ b/sample_profiles/group_keys
@@ -1,7 +1,7 @@
 # Sample profile by groups keys
 
 g logo 000096
-g indicators 00ffff
+g indicators ffffff
 g multimedia 009600
 g fkeys ff00ff
 g modifiers ff0000
@@ -9,5 +9,6 @@ g arrows ffff00
 g numeric 00ffff
 g functions ffffff
 g keys 009696
+g gkeys ffffff
 
 c # Commit changes
diff --git a/sample_profiles/keys_v_gradiant_fr_ch-latin1 b/sample_profiles/keys_v_gradiant_fr_ch-latin1
index 0a56625..f22b5f5 100644
--- a/sample_profiles/keys_v_gradiant_fr_ch-latin1
+++ b/sample_profiles/keys_v_gradiant_fr_ch-latin1
@@ -56,7 +56,7 @@ k w $raw3
 k e $raw3
 k r $raw3
 k t $raw3
-k z $raw3
+k y $raw3
 k u $raw3
 k i $raw3
 k o $raw3
@@ -94,7 +94,7 @@ k num_plus $raw4
 # Raw 5 :
 k shift_left $raw5
 k intl_backslash $raw5
-k y $raw5
+k z $raw5
 k x $raw5
 k c $raw5
 k v $raw5
diff --git a/src/classes/Keyboard.cpp b/src/classes/Keyboard.cpp
index 43151f9..9c54f9d 100644
--- a/src/classes/Keyboard.cpp
+++ b/src/classes/Keyboard.cpp
@@ -1,845 +1,897 @@
 #include "Keyboard.h"
-#include 
+
+#include 
 #include 
-#include 
-#include "libusb.h"
+#include 
+
+#if defined(hidapi)
+	#include 
+	#include "hidapi/hidapi.h"
+#elif defined(libusb)
+	#include "libusb-1.0/libusb.h"
+#endif
 
 
-bool Keyboard::isAttached() {
-	return m_isAttached;
+using namespace std;
+
+
+
+LedKeyboard::~LedKeyboard() {
+	close();
 }
 
-bool Keyboard::attach() {
-	if (m_isAttached == true) return false;
-	int r;
-	r = libusb_init(&ctx);
-	if (r < 0) return false;
-	
-	libusb_device **devs;
-	ssize_t cnt = libusb_get_device_list(ctx, &devs);
-	if(cnt < 0) return false;
-	int pid = 0;
-	for(ssize_t i = 0; i < cnt; i++) {
-		libusb_device *device = devs[i];
-		libusb_device_descriptor desc = {
-			0, // bLength
-			0, // bDescriptorType
-			0, // bcdUSB
-			0, // bDeviceClass
-			0, // bDeviceSubClass
-			0, // bDeviceProtocol
-			0, // bMaxPacketSize0
-			0, // idVendor
-			0, // idProduct
-			0, // bcdDevice
-			0, // iManufacturer
-			0, // iProduct
-			0, // iSerialNumber
-			0  // bNumConfigurations
-		};
-		libusb_get_device_descriptor(device, &desc);
-		if (desc.idVendor == 0x046d) {
-			if (desc.idProduct == 0xc331) { pid = desc.idProduct; break; } // G810 spectrum
-			if (desc.idProduct == 0xc337) { pid = desc.idProduct; break; } // G810 spectrum
-			if (desc.idProduct == 0xc330) { pid = desc.idProduct; break; } // G410 spectrum
-			if (desc.idProduct == 0xc333) { pid = desc.idProduct; break; } // G610 spectrum
-			if (desc.idProduct == 0xc32b) { // G910 spark
-				pid = desc.idProduct;
-				kbdProtocol = KeyboardProtocol::g910;
-				break;
-			}
-			if (desc.idProduct == 0xc335) { // G910 spectrum
-				pid = desc.idProduct;
-				kbdProtocol = KeyboardProtocol::g910;
-				break;
-			}
-		}
-	}
-	libusb_free_device_list(devs, 1);
-	if (pid == 0) {
-		libusb_exit(ctx);
-		ctx = NULL;
-		return false;
-	}
-	
-	dev_handle = libusb_open_device_with_vid_pid(ctx, 0x046d, pid);
-	if (dev_handle == NULL) {
-		libusb_exit(ctx);
-		ctx = NULL;
-		return false;
-	}
-	if(libusb_kernel_driver_active(dev_handle, 1) == 1) {
-		libusb_detach_kernel_driver(dev_handle, 1);
-		m_isKernellDetached = true;
-	}
-	r = libusb_claim_interface(dev_handle, 1);
-	if(r < 0) return false;
-	m_isAttached = true;
-	return true;
-}
 
-bool Keyboard::detach() {
-	if (m_isAttached == false) return false;
-	int r;
-	r = libusb_release_interface(dev_handle, 1);
-	if(r!=0) return false;
-	if(m_isKernellDetached==true) {
-		libusb_attach_kernel_driver(dev_handle, 1);
-		m_isKernellDetached = false;
-	}
-	libusb_close(dev_handle);
-	dev_handle = NULL;
-	libusb_exit(ctx);
-	ctx = NULL;
-	m_isAttached = false;
-	return true;
-}
+vector LedKeyboard::listKeyboards() {
+	vector deviceList;
 
-bool Keyboard::commit() {
-	if (m_isAttached == false) return false;
-	bool retval = false;
-	unsigned char *data = new unsigned char[20];
-	switch (kbdProtocol) {
-		case KeyboardProtocol::generic:
-			data[0] = 0x11;
-			data[1] = 0xff;
-			data[2] = 0x0c;
-			data[3] = 0x5a;
-			break;
-		case KeyboardProtocol::g910:
-			data[0] = 0x11;
-			data[1] = 0xff;
-			data[2] = 0x0f;
-			data[3] = 0x5d;
-			break;
-		default:
-			return false;
-			break;
-	}
-	for(int i = 4; i < 20; i++) data[i] = 0x00;
-	retval = sendDataInternal(data, 20);
-	delete[] data;
-	return retval;
-}
+	#if defined(hidapi)
+		if (hid_init() < 0) return deviceList;
+		
+		struct hid_device_info *devs, *dev;
+		devs = hid_enumerate(0x0, 0x0);
+		dev = devs;
+		while (dev) {
+			for (size_t i = 0; i < SupportedKeyboards.size(); i++) {
+				if (dev->vendor_id == SupportedKeyboards[i][0]) {
+					if (dev->product_id == SupportedKeyboards[i][1]) {
+						DeviceInfo deviceInfo;
+						deviceInfo.vendorID=dev->vendor_id;
+						deviceInfo.productID=dev->product_id;
 
-bool Keyboard::getKeyAddress(Key key, KeyAddress &keyAddress) {
-	switch (key) {
-		case Key::logo:
-			keyAddress.addressGroup = KeyAddressGroup::logo;
-			keyAddress.id = 0x01;
-			break;
-		case Key::logo2:
-			keyAddress.addressGroup = KeyAddressGroup::logo;
-			keyAddress.id = 0x02;
-			break;
-		case Key::backlight:
-			keyAddress.addressGroup = KeyAddressGroup::indicators;
-			keyAddress.id = 0x01;
-			break;
-		case Key::game:
-			keyAddress.addressGroup = KeyAddressGroup::indicators;
-			keyAddress.id = 0x02;
-			break;
-		case Key::caps:
-			keyAddress.addressGroup = KeyAddressGroup::indicators;
-			keyAddress.id = 0x03;
-			break;
-		case Key::scroll:
-			keyAddress.addressGroup = KeyAddressGroup::indicators;
-			keyAddress.id = 0x04;
-			break;
-		case Key::num:
-			keyAddress.addressGroup = KeyAddressGroup::indicators;
-			keyAddress.id = 0x05;
-			break;
-		case Key::next:
-			keyAddress.addressGroup = KeyAddressGroup::multimedia;
-			keyAddress.id = 0xb5;
-			break;
-		case Key::prev:
-			keyAddress.addressGroup = KeyAddressGroup::multimedia;
-			keyAddress.id = 0xb6;
-			break;
-		case Key::stop:
-			keyAddress.addressGroup = KeyAddressGroup::multimedia;
-			keyAddress.id = 0xb7;
-			break;
-		case Key::play:
-			keyAddress.addressGroup = KeyAddressGroup::multimedia;
-			keyAddress.id = 0xcd;
-			break;
-		case Key::mute:
-			keyAddress.addressGroup = KeyAddressGroup::multimedia;
-			keyAddress.id = 0xe2;
-			break;
-		case Key::g1:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x01;
-			break;
-		case Key::g2:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x02;
-			break;
-		case Key::g3:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x03;
-			break;
-		case Key::g4:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x04;
-			break;
-		case Key::g5:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x05;
-			break;
-		case Key::g6:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x06;
-			break;
-		case Key::g7:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x07;
-			break;
-		case Key::g8:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x08;
-			break;
-		case Key::g9:
-			keyAddress.addressGroup = KeyAddressGroup::gkeys;
-			keyAddress.id = 0x09;
-			break;
-		default:
-			keyAddress.addressGroup = KeyAddressGroup::keys;
-			switch (key) {
-				case Key::a: keyAddress.id = 0x04; break;
-				case Key::b: keyAddress.id = 0x05; break;
-				case Key::c: keyAddress.id = 0x06; break;
-				case Key::d: keyAddress.id = 0x07; break;
-				case Key::e: keyAddress.id = 0x08; break;
-				case Key::f: keyAddress.id = 0x09; break;
-				case Key::g: keyAddress.id = 0x0a; break;
-				case Key::h: keyAddress.id = 0x0b; break;
-				case Key::i: keyAddress.id = 0x0c; break;
-				case Key::j: keyAddress.id = 0x0d; break;
-				case Key::k: keyAddress.id = 0x0e; break;
-				case Key::l: keyAddress.id = 0x0f; break;
-				case Key::m: keyAddress.id = 0x10; break;
-				case Key::n: keyAddress.id = 0x11; break;
-				case Key::o: keyAddress.id = 0x12; break;
-				case Key::p: keyAddress.id = 0x13; break;
-				case Key::q: keyAddress.id = 0x14; break;
-				case Key::r: keyAddress.id = 0x15; break;
-				case Key::s: keyAddress.id = 0x16; break;
-				case Key::t: keyAddress.id = 0x17; break;
-				case Key::u: keyAddress.id = 0x18; break;
-				case Key::v: keyAddress.id = 0x19; break;
-				case Key::w: keyAddress.id = 0x1a; break;
-				case Key::x: keyAddress.id = 0x1b; break;
-				case Key::z: keyAddress.id = 0x1c; break;
-				case Key::y: keyAddress.id = 0x1d; break;
-				case Key::n1: keyAddress.id = 0x1e; break;
-				case Key::n2: keyAddress.id = 0x1f; break;
-				case Key::n3: keyAddress.id = 0x20; break;
-				case Key::n4: keyAddress.id = 0x21; break;
-				case Key::n5: keyAddress.id = 0x22; break;
-				case Key::n6: keyAddress.id = 0x23; break;
-				case Key::n7: keyAddress.id = 0x24; break;
-				case Key::n8: keyAddress.id = 0x25; break;
-				case Key::n9: keyAddress.id = 0x26; break;
-				case Key::n0: keyAddress.id = 0x27; break;
-				case Key::enter: keyAddress.id = 0x28; break;
-				case Key::esc: keyAddress.id = 0x29; break;
-				case Key::backspace: keyAddress.id = 0x2a; break;
-				case Key::tab: keyAddress.id = 0x2b; break;
-				case Key::space: keyAddress.id = 0x2c; break;
-				case Key::minus: keyAddress.id = 0x2d; break;
-				case Key::equal: keyAddress.id = 0x2e; break;
-				case Key::open_bracket: keyAddress.id = 0x2f; break;
-				case Key::close_bracket: keyAddress.id = 0x30; break;
-				case Key::backslash: keyAddress.id = 0x31; break;
-				case Key::dollar: keyAddress.id = 0x32; break;
-				case Key::semicolon: keyAddress.id = 0x33; break;
-				case Key::quote: keyAddress.id = 0x34; break;
-				case Key::tilde: keyAddress.id = 0x35; break;
-				case Key::comma: keyAddress.id = 0x36; break;
-				case Key::period: keyAddress.id = 0x37; break;
-				case Key::slash: keyAddress.id = 0x38; break;
-				case Key::caps_lock: keyAddress.id = 0x39; break;
-				case Key::f1: keyAddress.id = 0x3a; break;
-				case Key::f2: keyAddress.id = 0x3b; break;
-				case Key::f3: keyAddress.id = 0x3c; break;
-				case Key::f4: keyAddress.id = 0x3d; break;
-				case Key::f5: keyAddress.id = 0x3e; break;
-				case Key::f6: keyAddress.id = 0x3f; break;
-				case Key::f7: keyAddress.id = 0x40; break;
-				case Key::f8: keyAddress.id = 0x41; break;
-				case Key::f9: keyAddress.id = 0x42; break;
-				case Key::f10: keyAddress.id = 0x43; break;
-				case Key::f11: keyAddress.id = 0x44; break;
-				case Key::f12: keyAddress.id = 0x45; break;
-				case Key::print_screen: keyAddress.id = 0x46; break;
-				case Key::scroll_lock: keyAddress.id = 0x47; break;
-				case Key::pause_break: keyAddress.id = 0x48; break;
-				case Key::insert: keyAddress.id = 0x49; break;
-				case Key::home: keyAddress.id = 0x4a; break;
-				case Key::page_up: keyAddress.id = 0x4b; break;
-				case Key::del: keyAddress.id = 0x4c; break;
-				case Key::end: keyAddress.id = 0x4d; break;
-				case Key::page_down: keyAddress.id = 0x4e; break;
-				case Key::arrow_right: keyAddress.id = 0x4f; break;
-				case Key::arrow_left: keyAddress.id = 0x50; break;
-				case Key::arrow_bottom: keyAddress.id = 0x51; break;
-				case Key::arrow_top: keyAddress.id = 0x52; break;
-				case Key::num_lock: keyAddress.id = 0x53; break;
-				case Key::num_slash: keyAddress.id = 0x54; break;
-				case Key::num_asterisk: keyAddress.id = 0x55; break;
-				case Key::num_minus: keyAddress.id = 0x56; break;
-				case Key::num_plus: keyAddress.id = 0x57; break;
-				case Key::num_enter: keyAddress.id = 0x58; break;
-				case Key::num_1: keyAddress.id = 0x59; break;
-				case Key::num_2: keyAddress.id = 0x5a; break;
-				case Key::num_3: keyAddress.id = 0x5b; break;
-				case Key::num_4: keyAddress.id = 0x5c; break;
-				case Key::num_5: keyAddress.id = 0x5d; break;
-				case Key::num_6: keyAddress.id = 0x5e; break;
-				case Key::num_7: keyAddress.id = 0x5f; break;
-				case Key::num_8: keyAddress.id = 0x60; break;
-				case Key::num_9: keyAddress.id = 0x61; break;
-				case Key::num_0: keyAddress.id = 0x62; break;
-				case Key::num_dot: keyAddress.id = 0x63; break;
-				case Key::intl_backslash: keyAddress.id = 0x64; break;
-				case Key::menu: keyAddress.id = 0x65; break;
-				case Key::ctrl_left: keyAddress.id = 0xe0; break;
-				case Key::shift_left: keyAddress.id = 0xe1; break;
-				case Key::alt_left: keyAddress.id = 0xe2; break;
-				case Key::win_left: keyAddress.id = 0xe3; break;
-				case Key::ctrl_right: keyAddress.id = 0xe4; break;
-				case Key::shift_right: keyAddress.id = 0xe5; break;
-				case Key::alt_right: keyAddress.id = 0xe6; break;
-				case Key::win_right: keyAddress.id = 0xe7; break;
-				default: break;
-			}
-			break;
-	}
-	return true;
-}
+						if (dev->serial_number != NULL) {
+							char buf[256];
+							wcstombs(buf, dev->serial_number, 256);
+							deviceInfo.serialNumber = string(buf);
+						}
 
-bool Keyboard::parsePowerOnEffect(std::string effect, PowerOnEffect &powerOnEffect) {
-	if (effect == "rainbow") powerOnEffect = PowerOnEffect::rainbow;
-	else if (effect == "color") powerOnEffect = PowerOnEffect::color;
-	else return false;
-	return true;
-}
+						if (dev->manufacturer_string != NULL)
+						{
+							char buf[256];
+							wcstombs(buf, dev->manufacturer_string, 256);
+							deviceInfo.manufacturer = string(buf);
+						}
 
-bool Keyboard::parseKey(std::string key, KeyAddress &keyAddress) {
-	std::transform(key.begin(), key.end(), key.begin(), ::tolower);
-	Key parsedKey;
-	if (key == "logo") parsedKey = Key::logo;
-	else if (key == "logo2") parsedKey = Key::logo2;
-	else if (key == "back_light" || key == "backlight" || key == "light") parsedKey = Key::backlight;
-	else if (key == "game_mode" || key == "gamemode" || key == "game") parsedKey = Key::game;
-	else if (key == "caps_indicator" || key == "capsindicator" || key == "caps") parsedKey = Key::caps;
-	else if (key == "scroll_indicator" || key == "scrollindicator" || key == "scroll") parsedKey = Key::scroll;
-	else if (key == "num_indicator" || key == "numindicator" || key == "num") parsedKey = Key::num;
-	else if (key == "next") parsedKey = Key::next;
-	else if (key == "prev" || key == "previous") parsedKey = Key::prev;
-	else if (key == "stop") parsedKey = Key::stop;
-	else if (key == "play_pause" || key == "playpause" || key == "play") parsedKey = Key::play;
-	else if (key == "mute") parsedKey = Key::mute;
-	else if (key == "a") parsedKey = Key::a;
-	else if (key == "b") parsedKey = Key::b;
-	else if (key == "c") parsedKey = Key::c;
-	else if (key == "d") parsedKey = Key::d;
-	else if (key == "e") parsedKey = Key::e;
-	else if (key == "f") parsedKey = Key::f;
-	else if (key == "g") parsedKey = Key::g;
-	else if (key == "h") parsedKey = Key::h;
-	else if (key == "i") parsedKey = Key::i;
-	else if (key == "j") parsedKey = Key::j;
-	else if (key == "k") parsedKey = Key::k;
-	else if (key == "l") parsedKey = Key::l;
-	else if (key == "m") parsedKey = Key::m;
-	else if (key == "n") parsedKey = Key::n;
-	else if (key == "o") parsedKey = Key::o;
-	else if (key == "p") parsedKey = Key::p;
-	else if (key == "q") parsedKey = Key::q;
-	else if (key == "r") parsedKey = Key::r;
-	else if (key == "s") parsedKey = Key::s;
-	else if (key == "t") parsedKey = Key::t;
-	else if (key == "u") parsedKey = Key::u;
-	else if (key == "v") parsedKey = Key::v;
-	else if (key == "w") parsedKey = Key::w;
-	else if (key == "x") parsedKey = Key::x;
-	else if (key == "z") parsedKey = Key::z;
-	else if (key == "y") parsedKey = Key::y;
-	else if (key == "1" || key == "one") parsedKey = Key::n1;
-	else if (key == "2" || key == "two") parsedKey = Key::n2;
-	else if (key == "3" || key == "three") parsedKey = Key::n3;
-	else if (key == "4" || key == "four") parsedKey = Key::n4;
-	else if (key == "5" || key == "five") parsedKey = Key::n5;
-	else if (key == "6" || key == "six") parsedKey = Key::n6;
-	else if (key == "7" || key == "seven") parsedKey = Key::n7;
-	else if (key == "8" || key == "eight") parsedKey = Key::n8;
-	else if (key == "9" || key == "nine") parsedKey = Key::n9;
-	else if (key == "0" || key == "zero") parsedKey = Key::n0;
-	else if (key == "enter") parsedKey = Key::enter;
-	else if (key == "esc" || key == "escape") parsedKey = Key::esc;
-	else if (key == "back" || key == "backspace") parsedKey = Key::backspace;
-	else if (key == "tab") parsedKey = Key::tab;
-	else if (key == "space") parsedKey = Key::space;
-	else if (key == "tilde" || key == "~") parsedKey = Key::tilde;
-	else if (key == "minus" || key == "-") parsedKey = Key::minus;
-	else if (key == "equal" || key == "=") parsedKey = Key::equal;
-	else if (key == "open_bracket" || key == "[") parsedKey = Key::open_bracket;
-	else if (key == "close_bracket" || key == "]") parsedKey = Key::close_bracket;
-	else if (key == "backslash" || key == "\\") parsedKey = Key::backslash;
-	else if (key == "semicolon" || key == ";") parsedKey = Key::semicolon;
-	else if (key == "quote" || key == "\"") parsedKey = Key::quote;
-	else if (key == "dollar" || key == "$") parsedKey = Key::dollar;
-	else if (key == "comma" || key == ",") parsedKey = Key::comma;
-	else if (key == "period" || key == ".") parsedKey = Key::period;
-	else if (key == "slash" || key == "/") parsedKey = Key::slash;
-	else if (key == "caps_lock" || key == "capslock") parsedKey = Key::caps_lock;
-	else if (key == "f1") parsedKey = Key::f1;
-	else if (key == "f2") parsedKey = Key::f2;
-	else if (key == "f3") parsedKey = Key::f3;
-	else if (key == "f4") parsedKey = Key::f4;
-	else if (key == "f5") parsedKey = Key::f5;
-	else if (key == "f6") parsedKey = Key::f6;
-	else if (key == "f7") parsedKey = Key::f7;
-	else if (key == "f8") parsedKey = Key::f8;
-	else if (key == "f9") parsedKey = Key::f9;
-	else if (key == "f10") parsedKey = Key::f10;
-	else if (key == "f11") parsedKey = Key::f11;
-	else if (key == "f12") parsedKey = Key::f12;
-	else if (key == "print_screen" || key == "printscreen" || key == "printscr" || key == "print") parsedKey = Key::print_screen;
-	else if (key == "scroll_lock" || key == "scrolllock") parsedKey = Key::scroll_lock;
-	else if (key == "pause_break" || key == "pausebreak" || key == "pause" || key == "break") parsedKey = Key::pause_break;
-	else if (key == "insert" || key == "ins") parsedKey = Key::insert;
-	else if (key == "home") parsedKey = Key::home;
-	else if (key == "page_up" || key == "pageup") parsedKey = Key::page_up;
-	else if (key == "delete" || key == "del") parsedKey = Key::del;
-	else if (key == "end") parsedKey = Key::end;
-	else if (key == "page_down" || key == "pagedown") parsedKey = Key::page_down;
-	else if (key == "arrow_right" || key == "arrowright" || key == "right") parsedKey = Key::arrow_right;
-	else if (key == "arrow_left" || key == "arrowleft" || key == "left") parsedKey = Key::arrow_left;
-	else if (key == "arrow_bottom" || key == "arrowbottom" || key == "bottom") parsedKey = Key::arrow_bottom;
-	else if (key == "arrow_top" || key == "arrowtop" || key == "top") parsedKey = Key::arrow_top;
-	else if (key == "num_lock" || key == "numlock") parsedKey = Key::num_lock;
-	else if (key == "num/" || key == "num_slash" || key == "numslash") parsedKey = Key::num_slash;
-	else if (key == "num*" || key == "num_asterisk" || key == "numasterisk") parsedKey = Key::num_asterisk;
-	else if (key == "num-" || key == "num_minus" || key == "numminus") parsedKey = Key::num_minus;
-	else if (key == "num+" || key == "num_plus" || key == "numplus") parsedKey = Key::num_plus;
-	else if (key == "numenter") parsedKey = Key::num_enter;
-	else if (key == "num1") parsedKey = Key::num_1;
-	else if (key == "num2") parsedKey = Key::num_2;
-	else if (key == "num3") parsedKey = Key::num_3;
-	else if (key == "num4") parsedKey = Key::num_4;
-	else if (key == "num5") parsedKey = Key::num_5;
-	else if (key == "num6") parsedKey = Key::num_6;
-	else if (key == "num7") parsedKey = Key::num_7;
-	else if (key == "num8") parsedKey = Key::num_8;
-	else if (key == "num9") parsedKey = Key::num_9;
-	else if (key == "num0") parsedKey = Key::num_0;
-	else if (key == "num." || key == "num_period" || key == "numperiod") parsedKey = Key::num_dot;
-	else if (key == "intl_backslash" || key == "<") parsedKey = Key::intl_backslash;
-	else if (key == "menu") parsedKey = Key::menu;
-	else if (key == "ctrl_left" || key == "ctrlleft" || key == "ctrll") parsedKey = Key::ctrl_left;
-	else if (key == "shift_left" || key == "shiftleft" || key == "shiftl") parsedKey = Key::shift_left;
-	else if (key == "alt_left" || key == "altleft" || key == "altl") parsedKey = Key::alt_left;
-	else if (key == "win_left" || key == "winleft" || key == "winl") parsedKey = Key::win_left;
-	else if (key == "meta_left" || key == "metaleft" || key == "metal") parsedKey = Key::win_left;
-	else if (key == "ctrl_right" || key == "ctrlright" || key == "ctrlr") parsedKey = Key::ctrl_right;
-	else if (key == "shift_right" || key == "shiftright" || key == "shiftr") parsedKey = Key::shift_right;
-	else if (key == "alt_right" || key == "altright" || key == "altr" || key == "altgr") parsedKey = Key::alt_right;
-	else if (key == "win_right" || key == "winright" || key == "winr") parsedKey = Key::win_right;
-	else if (key == "meta_right" || key == "metaright" || key == "metar") parsedKey = Key::win_right;
-	else if (key == "g1") parsedKey = Key::g1;
-	else if (key == "g2") parsedKey = Key::g2;
-	else if (key == "g3") parsedKey = Key::g3;
-	else if (key == "g4") parsedKey = Key::g4;
-	else if (key == "g5") parsedKey = Key::g5;
-	else if (key == "g6") parsedKey = Key::g6;
-	else if (key == "g7") parsedKey = Key::g7;
-	else if (key == "g8") parsedKey = Key::g8;
-	else if (key == "g9") parsedKey = Key::g9;
-	else return false;
-	return getKeyAddress(parsedKey, keyAddress);
-}
+						if (dev->product_string != NULL)
+						{
+							char buf[256];
+							wcstombs(buf, dev->product_string, 256);
+							deviceInfo.product = string(buf);
+						}
 
-bool Keyboard::parseKeyGroup(std::string key, KeyGroup &keyGroup) {
-	if (key == "logo") keyGroup = KeyGroup::logo;
-	else if (key == "indicators") keyGroup = KeyGroup::indicators;
-	else if (key == "multimedia") keyGroup = KeyGroup::multimedia;
-	else if (key == "fkeys") keyGroup = KeyGroup::fkeys;
-	else if (key == "modifiers") keyGroup = KeyGroup::modifiers;
-	else if (key == "arrows") keyGroup = KeyGroup::arrows;
-	else if (key == "numeric") keyGroup = KeyGroup::numeric;
-	else if (key == "functions") keyGroup = KeyGroup::functions;
-	else if (key == "keys") keyGroup = KeyGroup::keys;
-	else if (key == "gkeys") keyGroup = KeyGroup::gkeys;
-	else return false;
-	return true;
-}
-
-bool Keyboard::parseColor(std::string color, KeyColors &colors) {
-	if (color.length() == 2) color = color + "0000";  // For G610
-	if (color.length() != 6) return false;
-	colors.red = std::stoul("0x"+color.substr(0,2), nullptr, 16);
-	colors.green = std::stoul("0x"+color.substr(2,2), nullptr, 16);
-	colors.blue = std::stoul("0x"+color.substr(4,2), nullptr, 16);
-	return true;
-}
-
-bool Keyboard::sendDataInternal(unsigned char *data, uint16_t data_size) {
-	if (m_isAttached == false) return false;
-	int r;
-	if (data_size > 20) r = libusb_control_transfer(dev_handle, 0x21, 0x09, 0x0212, 1, data, data_size, 2000);
-	else r = libusb_control_transfer(dev_handle, 0x21, 0x09, 0x0211, 1, data, data_size, 2000);
-	usleep(1000);
-	if (r < 0) return false;
-	return true;
-}
-
-bool Keyboard::populateAddressGroupInternal(KeyAddressGroup addressGroup, unsigned char *data) {
-	switch (kbdProtocol) {
-		case KeyboardProtocol::generic:
-			switch (addressGroup) {
-				case KeyAddressGroup::logo:
-					data[0] = 0x11;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0c;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x10;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x01;  // Base address
-					break;
-				case KeyAddressGroup::indicators:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0c;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x40;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x05;  // Base address
-					break;
-				case KeyAddressGroup::multimedia:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0c;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x02;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x05;  // Base address
-					break;
-				case KeyAddressGroup::keys:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0c;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x01;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x0e;  // Base address
-					break;
-				default:
-					return false;
-					break;
-			}
-			break;
-		case KeyboardProtocol::g910: // gkeys and mkeys seem not changeable
-			switch (addressGroup) {
-				case KeyAddressGroup::logo:
-					data[0] = 0x11;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0f;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x10;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x02;  // Base address
-					break;
-				case KeyAddressGroup::indicators:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0c;  // Base address
-					data[3] = 0x3a;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x40;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x05;  // Base address
-					break;
-				case KeyAddressGroup::keys:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0f;  // Base address
-					data[3] = 0x3d;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x01;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x0e;  // Base address
-					break;
-				case KeyAddressGroup::gkeys:
-					data[0] = 0x12;  // Base address
-					data[1] = 0xff;  // Base address
-					data[2] = 0x0f;  // Base address
-					data[3] = 0x3e;  // Base address
-					data[4] = 0x00;  // Base address
-					data[5] = 0x04;  // Base address
-					data[6] = 0x00;  // Base address
-					data[7] = 0x09;  // Base address
-					break;
-				default:
-					return false;
-					break;
-			}
-			break;
-		default:
-			return false;
-			break;
-	}
-	return true;
-}
-
-bool Keyboard::setKeysInternal(KeyAddressGroup addressGroup, KeyValue keyValues[], size_t keyValueCount) {
-	bool retval = false;
-	int data_size;
-	if (addressGroup == KeyAddressGroup::logo) data_size = 20;
-	else data_size = 64;
-	unsigned char *data = new unsigned char[data_size];
-	const size_t maxKeyValueCount = (data_size - 8) / 4;
-	populateAddressGroupInternal(addressGroup, data);
-	for(size_t i = 0; i < maxKeyValueCount; i++) {
-		if (i < keyValueCount) {
-			data[8 + i * 4 + 0] = keyValues[i].key.id;
-			data[8 + i * 4 + 1] = keyValues[i].colors.red;
-			data[8 + i * 4 + 2] = keyValues[i].colors.green;
-			data[8 + i * 4 + 3] = keyValues[i].colors.blue;
-		} else {
-			data[8 + i * 4 + 0] = 0x00;
-			data[8 + i * 4 + 1] = 0x00;
-			data[8 + i * 4 + 2] = 0x00;
-			data[8 + i * 4 + 3] = 0x00;
-		}
-	}
-	retval = sendDataInternal(data, data_size);
-	delete[] data;
-	return retval;
-}
-
-bool Keyboard::setPowerOnEffect(PowerOnEffect powerOnEffect) {
-	bool retval = false;
-	int data_size = 20;
-	unsigned char *data = new unsigned char[data_size];
-	data[0] = 0x11;  // Base address
-	data[1] = 0xff;  // Base address
-	data[2] = 0x0d;  // Base address
-	data[3] = 0x5a;  // Base address
-	data[4] = 0x00;  // Base address
-	data[5] = 0x01;  // Base address
-	switch (powerOnEffect) {
-		case PowerOnEffect::rainbow:
-			data[6] = 0x01;
-			break;
-		case PowerOnEffect::color:
-			data[6] = 0x02;
-			break;
-	}
-	for(int i = 7; i < data_size; i++) data[i] = 0x00;
-	retval = sendDataInternal(data, data_size);
-	delete[] data;
-	return retval;
-}
-
-bool Keyboard::setKey(KeyValue keyValue) {
-	bool retval = false;
-	unsigned char *data;
-	int data_size;
-	if (keyValue.key.addressGroup == KeyAddressGroup::logo) {
-		data_size = 20;
-		data = new unsigned char[data_size];
-		populateAddressGroupInternal(keyValue.key.addressGroup, data);
-	} else {
-		data_size = 64;
-		data = new unsigned char[data_size];
-		populateAddressGroupInternal(keyValue.key.addressGroup, data);
-	}
-	data[8] = keyValue.key.id;
-	data[9] = keyValue.colors.red;
-	data[10] = keyValue.colors.green;
-	data[11] = keyValue.colors.blue;
-	for(int i = 12; i < data_size; i++) data[i] = 0x00;
-	retval = sendDataInternal(data, data_size);
-	delete[] data;
-	return retval;
-}
-
-bool Keyboard::setKey(Key key, KeyColors colors) {
-	KeyValue keyValue;
-	getKeyAddress(key, keyValue.key);
-	keyValue.colors = colors;
-	return setKey(keyValue);
-}
-
-bool Keyboard::setKeys(KeyValue keyValue[], size_t keyValueCount) {
-	const size_t maxLogoKeys = 5;
-	const size_t maxIndicatorsKeys = 25;
-	const size_t maxMultimediaKeys = 25;
-	const size_t maxKeys = 200;
-	const size_t maxGKeys = 25;
-	size_t logoCount = 0;
-	size_t indicatorsCount = 0;
-	size_t multimediaCount = 0;
-	size_t keysCount = 0;
-	size_t gkeysCount = 0;
-	KeyValue logo[maxLogoKeys];
-	KeyValue indicators[maxIndicatorsKeys];
-	KeyValue multimedia[maxMultimediaKeys];
-	KeyValue keys[maxKeys];
-	KeyValue gkeys[maxGKeys];
-	
-	for (size_t i = 0; i < keyValueCount; i++) {
-		if(keyValue[i].key.addressGroup == KeyAddressGroup::logo && logoCount <= maxLogoKeys) {
-			logo[logoCount] = keyValue[i];
-			logoCount++;
-		} else if(keyValue[i].key.addressGroup == KeyAddressGroup::indicators && indicatorsCount <= maxIndicatorsKeys) {
-			indicators[indicatorsCount] = keyValue[i];
-			indicatorsCount++;
-		} else if(keyValue[i].key.addressGroup == KeyAddressGroup::multimedia && multimediaCount <= maxMultimediaKeys) {
-			multimedia[multimediaCount] = keyValue[i];
-			multimediaCount++;
-		} else if(keyValue[i].key.addressGroup == KeyAddressGroup::keys && keysCount <= maxKeys) {
-			keys[keysCount] = keyValue[i];
-			keysCount++;
-		} else if(keyValue[i].key.addressGroup == KeyAddressGroup::gkeys && gkeysCount <= maxGKeys) {
-			gkeys[gkeysCount] = keyValue[i];
-			gkeysCount++;
-		}
-	}
-	
-	if (logoCount > 0) setKeysInternal(KeyAddressGroup::logo, logo, logoCount);
-	
-	if (indicatorsCount > 0) setKeysInternal(KeyAddressGroup::indicators, indicators, indicatorsCount);
-	
-	if (multimediaCount > 0) setKeysInternal(KeyAddressGroup::multimedia, multimedia, multimediaCount);
-	
-	if (keysCount > 0) {
-		const size_t maxKeyValueCount = 14;
-		for (size_t i = 0; i < keysCount; i = i + maxKeyValueCount) {
-			KeyValue keysBlock[maxKeyValueCount];
-			size_t keysBlockCount = 0;
-			for (size_t j = 0; j < maxKeyValueCount; j++) {
-				if((i + j) < keysCount ) {
-					keysBlock[j] = keys[i + j];
-					keysBlockCount++;
+						deviceList.push_back(deviceInfo);
+						dev = dev->next;
+						break;
+					}
 				}
 			}
-			setKeysInternal(KeyAddressGroup::keys, keysBlock, keysBlockCount);
+			if (dev != NULL) dev = dev->next;
+		}
+		hid_free_enumeration(devs);
+		hid_exit();
+		
+	#elif defined(libusb)
+		libusb_context *ctx = NULL;
+		if(libusb_init(&m_ctx) < 0) return deviceList;
+		
+		libusb_device **devs;
+		ssize_t cnt = libusb_get_device_list(ctx, &devs);
+		for(ssize_t i = 0; i < cnt; i++) {
+			libusb_device *device = devs[i];
+			libusb_device_descriptor desc;
+			libusb_get_device_descriptor(device, &desc);
+			for (int i=0; i<(int)SupportedKeyboards.size(); i++) {
+				if (desc.idVendor == SupportedKeyboards[i][0]) {
+					if (desc.idProduct == SupportedKeyboards[i][1]) {
+					  unsigned char buf[256];
+						DeviceInfo deviceInfo;
+						deviceInfo.vendorID=desc.idVendor;
+						deviceInfo.productID=desc.idProduct;
+
+						if (libusb_open(device, &m_hidHandle) != 0)	continue;
+
+						if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iSerialNumber, buf, 256) >= 1) deviceInfo.serialNumber = string((char*)buf);
+						if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iManufacturer, buf, 256) >= 1) deviceInfo.manufacturer = string((char*)buf);
+						if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iProduct, buf, 256) >= 1) deviceInfo.product = string((char*)buf);
+
+						deviceList.push_back(deviceInfo);
+						libusb_close(m_hidHandle);
+						m_hidHandle = NULL;
+						break;
+					}
+				}
+			}
+		}
+		libusb_free_device_list(devs, 1);
+
+		if (m_hidHandle != NULL) {
+			libusb_close(m_hidHandle);
+			m_hidHandle = NULL;
+		}
+		
+		libusb_exit(m_ctx);
+		m_ctx = NULL;
+	#endif
+	
+	return deviceList;
+}
+
+
+bool LedKeyboard::isOpen() {
+	return m_isOpen;
+}
+
+bool LedKeyboard::open() {
+	if (m_isOpen) return true;
+	
+	return open(0x0, 0x0, "");
+}
+
+bool LedKeyboard::open(uint16_t vendorID, uint16_t productID, string serial) {
+	if (m_isOpen && ! close()) return false;
+	currentDevice.model = KeyboardModel::unknown;
+
+	#if defined(hidapi)
+		if (hid_init() < 0) return false;
+
+		struct hid_device_info *devs, *dev;
+		devs = hid_enumerate(vendorID, productID);
+		dev = devs;
+		wstring wideSerial;
+
+		if (!serial.empty()) {
+			wchar_t tempSerial[256];
+			if (mbstowcs(tempSerial, serial.c_str(), 256) < 1) return false;
+			wideSerial = wstring(tempSerial);
+		}
+
+		while (dev) {
+			for (int i=0; i<(int)SupportedKeyboards.size(); i++) {
+				if (dev->vendor_id == SupportedKeyboards[i][0] && dev->product_id == SupportedKeyboards[i][1]) {
+					if (!serial.empty() && dev->serial_number != NULL && wideSerial.compare(dev->serial_number) != 0) break; //Serial didn't match
+
+					if (dev->serial_number != NULL) {
+						char buf[256];
+						wcstombs(buf,dev->serial_number,256);
+						currentDevice.serialNumber=string(buf);
+					}
+
+					if (dev->manufacturer_string != NULL)
+					{
+						char buf[256];
+						wcstombs(buf,dev->manufacturer_string,256);
+						currentDevice.manufacturer = string(buf);
+					}
+
+					if (dev->product_string != NULL)
+					{
+						char buf[256];
+						wcstombs(buf,dev->product_string,256);
+						currentDevice.product = string(buf);
+					}
+
+					currentDevice.vendorID = dev->vendor_id;
+					currentDevice.productID = dev->product_id;
+					currentDevice.model = (KeyboardModel)SupportedKeyboards[i][2];
+					break;
+				}
+			}
+			if (currentDevice.model != KeyboardModel::unknown) break;
+			dev = dev->next;
+		}
+
+		hid_free_enumeration(devs);
+
+		if (! dev) {
+			currentDevice.model = KeyboardModel::unknown;
+			hid_exit();
+			return false;
+		}
+
+		if (wideSerial.empty()) m_hidHandle = hid_open(currentDevice.vendorID, currentDevice.productID, NULL);
+		else m_hidHandle = hid_open(currentDevice.vendorID, currentDevice.productID, wideSerial.c_str());
+
+		if(m_hidHandle == 0) {
+			hid_exit();
+			return false;
+		}
+
+		m_isOpen = true;
+		return true;
+
+	#elif defined(libusb)
+		if (libusb_init(&m_ctx) < 0) return false;
+			
+		libusb_device **devs;
+		libusb_device *dev = NULL;
+		ssize_t cnt = libusb_get_device_list(m_ctx, &devs);
+		if(cnt >= 0) {
+			for(ssize_t i = 0; i < cnt; i++) {
+				libusb_device *device = devs[i];
+				libusb_device_descriptor desc;
+				libusb_get_device_descriptor(device, &desc);
+
+				if (vendorID != 0x0 && desc.idVendor != vendorID) continue;
+				else if (productID != 0x0 && desc.idProduct != productID) continue;
+				else if (! serial.empty()) {
+					if (desc.iSerialNumber <= 0) continue; //Device does not populate serial number
+
+					unsigned char buf[256];
+					if (libusb_open(device, &m_hidHandle) != 0){
+						m_hidHandle = NULL;
+						continue;
+					}
+
+					if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iSerialNumber, buf, 256) >= 1 && serial.compare((char*)buf) == 0) {
+						//Make sure entry is a supported keyboard and get model
+						for (int i=0; i<(int)SupportedKeyboards.size(); i++) {
+							if (desc.idVendor == SupportedKeyboards[i][0]) {
+								if (desc.idProduct == SupportedKeyboards[i][1]) {
+									if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iManufacturer, buf, 256) >= 1) currentDevice.manufacturer = string((char*)buf);
+									if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iProduct, buf, 256) >= 1) currentDevice.product = string((char*)buf);
+									currentDevice.serialNumber = serial;
+									currentDevice.vendorID = desc.idVendor;
+									currentDevice.productID = desc.idProduct;
+									currentDevice.model = (KeyboardModel)SupportedKeyboards[i][2];
+
+									dev = device;
+									libusb_close(m_hidHandle);
+									m_hidHandle = NULL;
+									break;
+								}
+							}
+						}
+					}
+					else {
+						libusb_close(m_hidHandle);
+						m_hidHandle = NULL;
+						continue; //Serial number set but doesn't match
+					}
+				}
+
+				//For the case where serial is not specified, find first supported device
+				for (int i=0; i<(int)SupportedKeyboards.size(); i++) {
+					if (desc.idVendor == SupportedKeyboards[i][0]) {
+						if (desc.idProduct == SupportedKeyboards[i][1]) {
+							unsigned char buf[256];
+							if (libusb_open(device, &m_hidHandle) != 0){
+								m_hidHandle = NULL;
+								continue;
+							}
+							currentDevice.vendorID = desc.idVendor;
+							currentDevice.productID = desc.idProduct;
+							currentDevice.model = (KeyboardModel)SupportedKeyboards[i][2];
+							if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iManufacturer, buf, 256) >= 1) currentDevice.manufacturer = string((char*)buf);
+							if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iProduct, buf, 256) >= 1) currentDevice.product = string((char*)buf);
+							if (libusb_get_string_descriptor_ascii(m_hidHandle, desc.iSerialNumber, buf, 256) >= 1) currentDevice.serialNumber = string((char*)buf);
+
+							libusb_close(m_hidHandle);
+							m_hidHandle=NULL;
+							break;
+						}
+					}
+				}
+				if (currentDevice.model != KeyboardModel::unknown) break;
+			}
+			libusb_free_device_list(devs, 1);
+		}
+
+		if (currentDevice.model == KeyboardModel::unknown) {
+			libusb_exit(m_ctx);
+			m_ctx = NULL;
+			return false;
+		}
+			
+		if (dev == NULL) m_hidHandle = libusb_open_device_with_vid_pid(m_ctx, currentDevice.vendorID, currentDevice.productID);
+		else libusb_open(dev, &m_hidHandle);
+
+		if(m_hidHandle == NULL) {
+			libusb_exit(m_ctx);
+			m_ctx = NULL;
+			return false;
+		}
+			
+		if(libusb_kernel_driver_active(m_hidHandle, 1) == 1) {
+			if(libusb_detach_kernel_driver(m_hidHandle, 1) != 0) {
+				libusb_exit(m_ctx);
+				m_ctx = NULL;
+				return false;
+			}
+			m_isKernellDetached = true;
+		}
+			
+		if(libusb_claim_interface(m_hidHandle, 1) < 0) {
+			if(m_isKernellDetached==true) {
+				libusb_attach_kernel_driver(m_hidHandle, 1);
+				m_isKernellDetached = false;
+			}
+			libusb_exit(m_ctx);
+			m_ctx = NULL;
+			return false;
+		}
+			
+		m_isOpen = true;
+		return true;
+	#endif
+
+	return false; //In case neither is defined
+}
+
+LedKeyboard::DeviceInfo LedKeyboard::getCurrentDevice() {
+	return currentDevice;
+}
+
+bool LedKeyboard::close() {
+	if (! m_isOpen) return true;
+	m_isOpen = false;
+	
+	#if defined(hidapi)
+		hid_close(m_hidHandle);
+		m_hidHandle = NULL;
+		hid_exit();
+		return true;
+	#elif defined(libusb)
+		if (m_hidHandle == NULL) return true;
+		if(libusb_release_interface(m_hidHandle, 1) != 0) return false;
+		if(m_isKernellDetached==true) {
+			libusb_attach_kernel_driver(m_hidHandle, 1);
+			m_isKernellDetached = false;
+		}
+		libusb_close(m_hidHandle);
+		m_hidHandle = NULL;
+		libusb_exit(m_ctx);
+		m_ctx = NULL;
+		return true;
+	#endif
+	
+	return false;
+}
+
+
+LedKeyboard::KeyboardModel LedKeyboard::getKeyboardModel() {
+	return currentDevice.model;
+}
+
+bool LedKeyboard::commit() {
+	byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+		case KeyboardModel::g413:
+			return true; // Keyboard is non-transactional
+		case KeyboardModel::g410:
+    case KeyboardModel::g513:
+		case KeyboardModel::g610:
+		case KeyboardModel::g810:
+		case KeyboardModel::gpro:
+			data = { 0x11, 0xff, 0x0c, 0x5a };
+			break;
+		case KeyboardModel::g910:
+			data = { 0x11, 0xff, 0x0f, 0x5d };
+			break;
+		default:
+			return false;
+	}
+	data.resize(20, 0x00);
+	return sendDataInternal(data);
+}
+
+bool LedKeyboard::setKey(LedKeyboard::KeyValue keyValue) {
+	return setKeys(KeyValueArray {keyValue});
+}
+
+bool LedKeyboard::setKeys(KeyValueArray keyValues) {
+	if (keyValues.empty()) return false;
+	
+	bool retval = true;
+	
+	vector> SortedKeys = {
+		{}, // Logo AddressGroup
+		{}, // Indicators AddressGroup
+		{}, // Multimedia AddressGroup
+		{}, // GKeys AddressGroup
+		{} // Keys AddressGroup
+	};
+	
+	for (uint8_t i = 0; i < keyValues.size(); i++) {
+		switch(static_cast(static_cast(keyValues[i].key) >> 8 )) {
+			case LedKeyboard::KeyAddressGroup::logo:
+				switch (currentDevice.model) {
+					case LedKeyboard::KeyboardModel::g610:
+					case LedKeyboard::KeyboardModel::g810:
+					case LedKeyboard::KeyboardModel::gpro:
+						if (SortedKeys[0].size() <= 1 && keyValues[i].key == LedKeyboard::Key::logo)
+							SortedKeys[0].push_back(keyValues[i]);
+						break;
+					case LedKeyboard::KeyboardModel::g910:
+						if (SortedKeys[0].size() <= 2) SortedKeys[0].push_back(keyValues[i]);
+						break;
+					default:
+						break;
+				}
+				break;
+			case LedKeyboard::KeyAddressGroup::indicators:
+				if (SortedKeys[1].size() <= 5) SortedKeys[1].push_back(keyValues[i]);
+				break;
+			case LedKeyboard::KeyAddressGroup::multimedia:
+				switch (currentDevice.model) {
+					case LedKeyboard::KeyboardModel::g610:
+					case LedKeyboard::KeyboardModel::g810:
+					case LedKeyboard::KeyboardModel::gpro:
+						if (SortedKeys[2].size() <= 5) SortedKeys[2].push_back(keyValues[i]);
+						break;
+					default:
+						break;
+				}
+				break;
+			case LedKeyboard::KeyAddressGroup::gkeys:
+				switch (currentDevice.model) {
+					case LedKeyboard::KeyboardModel::g910:
+						if (SortedKeys[3].size() <= 9) SortedKeys[3].push_back(keyValues[i]);
+						break;
+					default:
+						break;
+				}
+				break;
+			case LedKeyboard::KeyAddressGroup::keys:
+				switch (currentDevice.model) {
+					case LedKeyboard::KeyboardModel::g610:
+					case LedKeyboard::KeyboardModel::g810:
+					case LedKeyboard::KeyboardModel::g910:
+					case LedKeyboard::KeyboardModel::gpro:
+						if (SortedKeys[4].size() <= 120) SortedKeys[4].push_back(keyValues[i]);
+						break;
+					case LedKeyboard::KeyboardModel::g410:
+						if (SortedKeys[4].size() <= 120)
+							if (keyValues[i].key < LedKeyboard::Key::num_lock || 
+							    keyValues[i].key > LedKeyboard::Key::num_dot)
+								SortedKeys[4].push_back(keyValues[i]);
+						break;
+					default:
+						break;
+				}
+				break;
 		}
 	}
 	
-	if (gkeysCount > 0) setKeysInternal(KeyAddressGroup::gkeys, gkeys, gkeysCount);
-	
-	return true;
-}
-
-bool Keyboard::setAllKeys(KeyColors colors) {
-	KeyValue keyValues[127];
-	for (int i = 0; i < 127; i++) {
-		getKeyAddress((Key)i, keyValues[i].key);
-		keyValues[i].colors = colors;
+	for (uint8_t kag = 0; kag < 5; kag++) {
+		
+		if (SortedKeys[kag].size() > 0) {
+			
+			uint8_t gi = 0;
+			while (gi < SortedKeys[kag].size()) {
+				
+				size_t data_size = 0;
+				byte_buffer_t data = {};
+				
+				switch (kag) {
+					case 0:
+						data_size = 20;
+						data = getKeyGroupAddress(LedKeyboard::KeyAddressGroup::logo);
+						break;
+					case 1:
+						data_size = 64;
+						data = getKeyGroupAddress(LedKeyboard::KeyAddressGroup::indicators);
+						break;
+					case 2:
+						data_size = 64;
+						data = getKeyGroupAddress(LedKeyboard::KeyAddressGroup::multimedia);
+						break;
+					case 3:
+						data_size = 64;
+						data = getKeyGroupAddress(LedKeyboard::KeyAddressGroup::gkeys);
+						break;
+					case 4:
+						data_size = 64;
+						data = getKeyGroupAddress(LedKeyboard::KeyAddressGroup::keys);
+						break;
+				}
+				
+				const uint8_t maxKeyCount = (data_size - 8) / 4;
+				
+				if (data.size() > 0) {
+					
+					for (uint8_t i = 0; i < maxKeyCount; i++) {
+						if (gi + i < SortedKeys[kag].size()) {
+							data.push_back(static_cast(
+								static_cast(SortedKeys[kag][gi+i].key) & 0x00ff));
+							data.push_back(SortedKeys[kag][gi+i].color.red);
+							data.push_back(SortedKeys[kag][gi+i].color.green);
+							data.push_back(SortedKeys[kag][gi+i].color.blue);
+						}
+					}
+					
+					data.resize(data_size, 0x00);
+					
+					if (retval) retval = sendDataInternal(data);
+					else sendDataInternal(data);
+					
+				}
+				
+				gi = gi + maxKeyCount;
+			}
+			
+		}
 	}
-	setKeys(keyValues, 127);
-	return true;
+	return retval;
 }
 
-bool Keyboard::setGroupKeys(KeyGroup keyGroup, KeyColors colors) {
-	KeyValue keyValues[54];
-	int keyValuesCount = 0;
+bool LedKeyboard::setGroupKeys(KeyGroup keyGroup, LedKeyboard::Color color) {
+	KeyValueArray keyValues;
+	
+	KeyArray keyArray;
+	
 	switch (keyGroup) {
 		case KeyGroup::logo:
-			for (int i = 0; i < 2; i++) {
-				getKeyAddress((Key)i, keyValues[i].key);
-				keyValues[i].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
+			keyArray = keyGroupLogo;
 			break;
 		case KeyGroup::indicators:
-			for (int i = 2; i < 7; i++) {
-				getKeyAddress((Key)i, keyValues[i - 2].key);
-				keyValues[i - 2].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::multimedia:
-			for (int i = 7; i < 12; i++) {
-				getKeyAddress((Key)i, keyValues[i - 7].key);
-				keyValues[i - 7].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::fkeys:
-			for (int i = 12; i < 24; i++) {
-				getKeyAddress((Key)i, keyValues[i - 12].key);
-				keyValues[i - 12].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::modifiers:
-			for (int i = 24; i < 33; i++) {
-				getKeyAddress((Key)i, keyValues[i - 24].key);
-				keyValues[i - 24].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::arrows:
-			for (int i = 33; i < 37; i++) {
-				getKeyAddress((Key)i, keyValues[i - 33].key);
-				keyValues[i - 33].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::numeric:
-			for (int i = 37; i < 54; i++) {
-				getKeyAddress((Key)i, keyValues[i - 37].key);
-				keyValues[i - 37].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::functions:
-			for (int i = 54; i < 64; i++) {
-				getKeyAddress((Key)i, keyValues[i - 54].key);
-				keyValues[i - 54].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
-			break;
-		case KeyGroup::keys:
-			for (int i = 64; i < 118; i++) {
-				getKeyAddress((Key)i, keyValues[i - 64].key);
-				keyValues[i - 64].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
+			keyArray = keyGroupIndicators;
 			break;
 		case KeyGroup::gkeys:
-			for (int i = 118; i < 127; i++) {
-				getKeyAddress((Key)i, keyValues[i - 118].key);
-				keyValues[i - 118].colors = colors;
-				keyValuesCount++;
-			}
-			setKeys(keyValues, keyValuesCount);
+			keyArray = keyGroupGKeys;
+			break;
+		case KeyGroup::multimedia:
+			keyArray = keyGroupMultimedia;
+			break;
+		case KeyGroup::fkeys:
+			keyArray = keyGroupFKeys;
+			break;
+		case KeyGroup::modifiers:
+			keyArray = keyGroupModifiers;
+			break;
+		case KeyGroup::arrows:
+			keyArray = keyGroupArrows;
+			break;
+		case KeyGroup::numeric:
+			keyArray = keyGroupNumeric;
+			break;
+		case KeyGroup::functions:
+			keyArray = keyGroupFunctions;
+			break;
+		case KeyGroup::keys:
+			keyArray = keyGroupKeys;
+			break;
+		default:
 			break;
 	}
-	return true;
+	
+	for (uint8_t i = 0; i < keyArray.size(); i++) keyValues.push_back({keyArray[i], color});
+	
+	return setKeys(keyValues);
+}
+
+bool LedKeyboard::setAllKeys(LedKeyboard::Color color) {
+	KeyValueArray keyValues;
+
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+			for (uint8_t rIndex=0x01; rIndex <= 0x05; rIndex++) if (! setRegion(rIndex, color)) return false;
+			return true;
+		case KeyboardModel::g413:
+			setNativeEffect(NativeEffect::color, NativeEffectPart::keys, 0, color);
+			return true;
+		case KeyboardModel::g410:
+    case KeyboardModel::g513:
+		case KeyboardModel::g610:
+		case KeyboardModel::g810:
+		case KeyboardModel::g910:
+		case KeyboardModel::gpro:
+			for (uint8_t i = 0; i < keyGroupLogo.size(); i++) keyValues.push_back({keyGroupLogo[i], color});
+			for (uint8_t i = 0; i < keyGroupIndicators.size(); i++) keyValues.push_back({keyGroupIndicators[i], color});
+			for (uint8_t i = 0; i < keyGroupMultimedia.size(); i++) keyValues.push_back({keyGroupMultimedia[i], color});
+			for (uint8_t i = 0; i < keyGroupGKeys.size(); i++) keyValues.push_back({keyGroupGKeys[i], color});
+			for (uint8_t i = 0; i < keyGroupFKeys.size(); i++) keyValues.push_back({keyGroupFKeys[i], color});
+			for (uint8_t i = 0; i < keyGroupFunctions.size(); i++) keyValues.push_back({keyGroupFunctions[i], color});
+			for (uint8_t i = 0; i < keyGroupArrows.size(); i++) keyValues.push_back({keyGroupArrows[i], color});
+			for (uint8_t i = 0; i < keyGroupNumeric.size(); i++) keyValues.push_back({keyGroupNumeric[i], color});
+			for (uint8_t i = 0; i < keyGroupModifiers.size(); i++) keyValues.push_back({keyGroupModifiers[i], color});
+			for (uint8_t i = 0; i < keyGroupKeys.size(); i++) keyValues.push_back({keyGroupKeys[i], color});
+			return setKeys(keyValues);
+		default:
+			return false;
+	}
+	return false;
+}
+
+
+bool LedKeyboard::setMRKey(uint8_t value) {
+	LedKeyboard::byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g910:
+			switch (value) {
+				case 0x00:
+				case 0x01:
+					data = { 0x11, 0xff, 0x0a, 0x0e, value };
+					data.resize(20, 0x00);
+					return sendDataInternal(data);
+				default:
+					break;
+			}
+			break;
+		default:
+			break;
+	}
+	return false;
+}
+
+bool LedKeyboard::setMNKey(uint8_t value) {
+	LedKeyboard::byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g910:
+			switch (value) {
+				case 0x00:
+				case 0x01:
+				case 0x02:
+				case 0x03:
+				case 0x04:
+				case 0x05:
+				case 0x06:
+				case 0x07:
+					data = { 0x11, 0xff, 0x09, 0x1e, value };
+					data.resize(20, 0x00);
+					return sendDataInternal(data);
+				default:
+					break;
+			}
+			break;
+		default:
+			break;
+	}
+	return false;
+}
+
+bool LedKeyboard::setGKeysMode(uint8_t value) {
+	LedKeyboard::byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g910:
+			switch (value) {
+				case 0x00:
+				case 0x01:
+					data = { 0x11, 0xff, 0x08, 0x2e, value };
+					data.resize(20, 0x00);
+					return sendDataInternal(data);
+				default:
+					break;
+			}
+			break;
+		default:
+			break;
+	}
+	return false;
+}
+
+bool LedKeyboard::setRegion(uint8_t region, LedKeyboard::Color color) {
+	LedKeyboard::byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+			data = { 0x11, 0xff, 0x0c, 0x3a, region, 0x01, color.red, color.green, color.blue };
+			data.resize(20,0x00);
+			return sendDataInternal(data);
+			break;
+		default:
+			break;
+	}
+
+	return false;
+}
+
+bool LedKeyboard::setStartupMode(StartupMode startupMode) {
+	byte_buffer_t data;
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+		case KeyboardModel::g410:
+		case KeyboardModel::g610:
+		case KeyboardModel::g810:
+		case KeyboardModel::gpro:
+			data = { 0x11, 0xff, 0x0d, 0x5a, 0x00, 0x01 };
+			break;
+		case KeyboardModel::g910:
+			data = { 0x11, 0xff, 0x10, 0x5e, 0x00, 0x01 };
+			break;
+		default:
+			return false;
+	}
+	data.push_back((unsigned char)startupMode);
+	data.resize(20, 0x00);
+	return sendDataInternal(data);
+}
+
+
+bool LedKeyboard::setNativeEffect(NativeEffect effect, NativeEffectPart part, uint8_t speed, Color color) {
+	uint8_t protocolByte = 0;
+
+	// NativeEffectPart::all is not in the device protocol, but an alias for both keys and logo, plus indicators
+	if (part == LedKeyboard::NativeEffectPart::all) {
+		switch (effect) {
+			case LedKeyboard::NativeEffect::color:
+				if (! setGroupKeys(LedKeyboard::KeyGroup::indicators, color)) return false;
+				if (! commit()) return false;
+				break;
+			case LedKeyboard::NativeEffect::breathing:
+				if (! setGroupKeys(LedKeyboard::KeyGroup::indicators, color)) return false;;
+				if (! commit()) return false;;
+				break;
+			case LedKeyboard::NativeEffect::cycle:
+			case LedKeyboard::NativeEffect::hwave:
+			case LedKeyboard::NativeEffect::vwave:
+			case LedKeyboard::NativeEffect::cwave:
+				if (! setGroupKeys(
+					LedKeyboard::KeyGroup::indicators,
+					LedKeyboard::Color({0xff, 0xff, 0xff}))
+				) return false;
+				if (! commit()) return false;
+				break;
+		}
+		return (
+			setNativeEffect(effect, LedKeyboard::NativeEffectPart::keys, speed, color) &&
+			setNativeEffect(effect, LedKeyboard::NativeEffectPart::logo, speed, color));
+	}
+
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+		case KeyboardModel::g413:
+			protocolByte = 0x0c;
+			if (part == NativeEffectPart::logo) return true; //Does not have logo component
+			break;
+		case KeyboardModel::g410:
+    case KeyboardModel::g513:
+		case KeyboardModel::g610: // Unconfirmed
+		case KeyboardModel::g810:
+		case KeyboardModel::gpro:
+			protocolByte = 0x0d;
+			break;
+		case KeyboardModel::g910:
+			protocolByte = 0x10;
+			break;
+		default:
+			return false;
+	}
+
+	byte_buffer_t data;
+
+	switch (effect) {
+
+		case NativeEffect::color:
+			data = { 0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x01, color.red, color.green, color.blue, 0x02 };
+			break;
+		case NativeEffect::breathing:
+			data = {
+				0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x02,
+				color.red, color.green, color.blue, speed, 
+				0x10, 0x00, 0x64 
+			};
+			break;
+		case NativeEffect::cycle:
+			data = {
+				0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x03,
+				0x00, 0x00, 0x00, 0x00, 0x00, speed, 0x00, 0x00, 0x64
+			};
+			break;
+		case NativeEffect::hwave:
+			switch (part) {
+				case NativeEffectPart::logo:
+					setNativeEffect(NativeEffect::color, part, 0, Color({0x00, 0xff, 0xff}));
+					break;
+				default:
+					data = {
+						0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x04,
+						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x64, speed
+					};
+					break;
+			}
+			break;
+		case NativeEffect::vwave:
+			switch (part) {
+				case NativeEffectPart::logo:
+					setNativeEffect(NativeEffect::color, part, 0, Color({0x00, 0xff, 0xff}));
+					break;
+				default:
+					data = {
+						0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x04,
+						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02, 0x64, speed
+					};
+					break;
+			}
+			break;
+		case NativeEffect::cwave:
+			switch (part) {
+				case NativeEffectPart::logo:
+					setNativeEffect(NativeEffect::color, part, 0, Color({0x00, 0xff, 0xff}));
+					break;
+				default:
+					data = {
+						0x11, 0xff, protocolByte, 0x3c, (uint8_t)part, 0x04,
+						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x03, 0x64, speed
+					};
+					break;
+			}
+			break;
+		
+		default:
+			return false;
+	}
+	
+	data.resize(20, 0x00);
+	return sendDataInternal(data);
+}
+
+
+bool LedKeyboard::sendDataInternal(byte_buffer_t &data) {
+	if (data.size() > 0) {
+		#if defined(hidapi)
+			if (! open(currentDevice.vendorID, currentDevice.productID, currentDevice.serialNumber)) return false;
+			data.insert(data.begin(), 0x00);
+			if (hid_write(m_hidHandle, const_cast(data.data()), data.size()) < 0) {
+				std::cout<<"Error: Can not write to hidraw, try with the libusb version"<(data2.data()), data2.size(), 1);
+			*/
+			return true;
+		#elif defined(libusb)
+			if (! m_isOpen) return false;
+			if (data.size() > 20) {
+				if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0212, 1, 
+						const_cast(data.data()), data.size(), 2000) < 0)
+					return false;
+			} else {
+				if(libusb_control_transfer(m_hidHandle, 0x21, 0x09, 0x0211, 1, 
+						const_cast(data.data()), data.size(), 2000) < 0)
+					return false;
+			}
+			usleep(1000);
+			unsigned char buffer[64];
+			int len = 0;
+			libusb_interrupt_transfer(m_hidHandle, 0x82, buffer, sizeof(buffer), &len, 1);
+			return true;
+		#endif
+	}
+	
+	return false;
+}
+
+LedKeyboard::byte_buffer_t LedKeyboard::getKeyGroupAddress(LedKeyboard::KeyAddressGroup keyAddressGroup) {
+	switch (currentDevice.model) {
+		case KeyboardModel::g213:
+		case KeyboardModel::g413:
+		  return {}; // Device doesn't support per-key setting
+		case KeyboardModel::g410:
+    case KeyboardModel::g513:
+		case KeyboardModel::g610:
+		case KeyboardModel::g810:
+		case KeyboardModel::gpro:
+			switch (keyAddressGroup) {
+				case LedKeyboard::KeyAddressGroup::logo:
+					return { 0x11, 0xff, 0x0c, 0x3a, 0x00, 0x10, 0x00, 0x01 };
+				case LedKeyboard::KeyAddressGroup::indicators:
+					return { 0x12, 0xff, 0x0c, 0x3a, 0x00, 0x40, 0x00, 0x05 };
+				case LedKeyboard::KeyAddressGroup::gkeys:
+					return {};
+				case LedKeyboard::KeyAddressGroup::multimedia:
+					return { 0x12, 0xff, 0x0c, 0x3a, 0x00, 0x02, 0x00, 0x05 };
+				case LedKeyboard::KeyAddressGroup::keys:
+					return { 0x12, 0xff, 0x0c, 0x3a, 0x00, 0x01, 0x00, 0x0e };
+			}
+			break;
+		case KeyboardModel::g910:
+			switch (keyAddressGroup) {
+				case LedKeyboard::KeyAddressGroup::logo:
+					return { 0x11, 0xff, 0x0f, 0x3a, 0x00, 0x10, 0x00, 0x02 };
+				case LedKeyboard::KeyAddressGroup::indicators:
+					return { 0x12, 0xff, 0x0c, 0x3a, 0x00, 0x40, 0x00, 0x05 };
+				case LedKeyboard::KeyAddressGroup::gkeys:
+					return { 0x12, 0xff, 0x0f, 0x3e, 0x00, 0x04, 0x00, 0x09 };
+				case LedKeyboard::KeyAddressGroup::multimedia:
+					return {};
+				case LedKeyboard::KeyAddressGroup::keys:
+					return { 0x12, 0xff, 0x0f, 0x3d, 0x00, 0x01, 0x00, 0x0e };
+			}
+			break;
+		default:
+			break;
+	}
+	return {};
 }
diff --git a/src/classes/Keyboard.h b/src/classes/Keyboard.h
index a2b563e..76e178f 100644
--- a/src/classes/Keyboard.h
+++ b/src/classes/Keyboard.h
@@ -1,70 +1,223 @@
-#ifndef DEF_KEYBOARD
-#define DEF_KEYBOARD
+#ifndef KEYBOARD_CLASS
+#define KEYBOARD_CLASS
 
 #include 
-#include 
+#include 
 
-class Keyboard {
+#if defined(hidapi)
+	#include "hidapi/hidapi.h"
+#elif defined(libusb)
+	#include "libusb-1.0/libusb.h"
+#endif
+
+
+class LedKeyboard {
 	
+	
+	private:
+		
+		enum class KeyAddressGroup : uint8_t {
+			logo = 0x00,
+			indicators,
+			multimedia,
+			gkeys,
+			keys
+		};
+		
+		
 	public:
 		
-		enum class KeyboardProtocol { generic, g910 };
-		enum class PowerOnEffect { rainbow, color };
-		enum class KeyAddressGroup { logo, indicators, multimedia, keys, gkeys };
-		enum class Key { // 127 items
-			logo, logo2,
-			caps, num, scroll, game, backlight,
-			mute, play, stop, prev, next,
-			f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12,
-			shift_left, ctrl_left, win_left, alt_left, alt_right, win_right, ctrl_right, shift_right, menu,
-			arrow_top, arrow_left, arrow_bottom, arrow_right,
-			num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0, num_dot, num_enter, num_plus, num_minus, num_asterisk, num_slash, num_lock,
-			esc, scroll_lock,
-			insert, del, home, end, page_up, page_down, print_screen, pause_break,
-			n1, n2, n3, n4, n5, n6, n7, n8, n9, n0,
-			tab, caps_lock, space, backspace, enter,
-			a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
-			tilde, minus, equal, 
-			open_bracket, close_bracket, backslash, 
-			semicolon, quote, dollar,
-			intl_backslash, comma, period, slash,
-			g1, g2, g3, g4, g5, g6, g7, g8, g9
+		std::vector> SupportedKeyboards = {
+			{ 0x46d, 0xc336, (u_int16_t)KeyboardModel::g213 },
+			{ 0x46d, 0xc330, (u_int16_t)KeyboardModel::g410 },
+			{ 0x46d, 0xc33a, (u_int16_t)KeyboardModel::g413 },
+			{ 0x46d, 0xc33c, (u_int16_t)KeyboardModel::g513 },
+			{ 0x46d, 0xc333, (u_int16_t)KeyboardModel::g610 },
+			{ 0x46d, 0xc338, (u_int16_t)KeyboardModel::g610 },
+			{ 0x46d, 0xc331, (u_int16_t)KeyboardModel::g810 },
+			{ 0x46d, 0xc337, (u_int16_t)KeyboardModel::g810 },
+			{ 0x46d, 0xc32b, (u_int16_t)KeyboardModel::g910 },
+			{ 0x46d, 0xc335, (u_int16_t)KeyboardModel::g910 },
+			{ 0x46d, 0xc339, (u_int16_t)KeyboardModel::gpro }
 		};
-		enum class KeyGroup { logo, indicators, multimedia, fkeys, modifiers, arrows, numeric, functions, keys, gkeys};
 		
-		struct KeyColors { uint8_t red; uint8_t green; uint8_t blue; };
-		struct KeyAddress { KeyAddressGroup addressGroup; uint8_t id; };
-		struct KeyValue { KeyAddress key; KeyColors colors; };
+		enum class KeyboardModel : uint8_t {
+			unknown = 0x00,
+			g213,
+			g410,
+			g413,
+      g513,
+			g610,
+			g810,
+			g910,
+			gpro
+		};
+		enum class StartupMode : uint8_t {
+			wave = 0x01,
+			color
+		};
+		enum class NativeEffect : uint8_t {
+			color = 0x01,
+			breathing,
+			cycle,
+			hwave,
+			vwave,
+			cwave
+		};
+		enum class NativeEffectPart : uint8_t {
+			all = 0xff,
+			keys = 0x00,
+			logo
+		};
+		enum class KeyGroup : uint8_t {
+			logo = 0x00,
+			indicators,
+			multimedia,
+			gkeys,
+			fkeys,
+			modifiers,
+			functions,
+			arrows,
+			numeric,
+			keys
+		};
+		enum class Key : uint16_t { // 127 items
+			
+			logo = static_cast(KeyAddressGroup::logo) << 8 | 0x01,
+			logo2,
+			
+			backlight = static_cast(KeyAddressGroup::indicators) << 8| 0x01,
+			game, caps, scroll, num, 
+			
+			next = static_cast(KeyAddressGroup::multimedia) << 8 | 0xb5,
+			prev, stop,
+			play = static_cast(KeyAddressGroup::multimedia) << 8 | 0xcd,
+			mute = static_cast(KeyAddressGroup::multimedia) << 8 | 0xe2,
+			
+			g1 = static_cast(KeyAddressGroup::gkeys) << 8 | 0x01,
+			g2, g3, g4, g5, g6, g7, g8, g9,
+			
+			a = static_cast(KeyAddressGroup::keys) << 8 | 0x04,
+			b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
+			n1, n2, n3, n4, n5, n6, n7, n8, n9, n0,
+			enter, esc, backspace, tab, space, minus, equal, open_bracket, close_bracket,
+			backslash, dollar, semicolon, quote, tilde, comma, period, slash, caps_lock,
+			f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12,
+			print_screen, scroll_lock, pause_break, insert, home, page_up, del, end, page_down,
+			arrow_right, arrow_left, arrow_bottom, arrow_top, num_lock, num_slash, num_asterisk,
+			num_minus, num_plus, num_enter,
+			num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0,
+			num_dot, intl_backslash, menu,
+			
+			ctrl_left = static_cast(KeyAddressGroup::keys) << 8 | 0xe0,
+			shift_left, alt_left, win_left,
+			ctrl_right, shift_right, alt_right, win_right
+			
+		};
+
+		typedef struct {
+			uint16_t vendorID = 0x0;
+			uint16_t productID = 0x0;
+			std::string manufacturer = "";
+			std::string product = "";
+			std::string serialNumber = "";
+			KeyboardModel model;
+		} DeviceInfo;
+		
+		struct Color {
+			uint8_t red;
+			uint8_t green;
+			uint8_t blue;
+		};
+		struct KeyValue {
+			LedKeyboard::Key key;
+			LedKeyboard::Color color;
+		};
+		
+		typedef std::vector KeyValueArray;
+		
+		
+		~LedKeyboard();
+		
+		
+		std::vector listKeyboards();
+		
+		bool isOpen();
+		bool open();
+		bool open(uint16_t vendorID, uint16_t productID, std::string serial);
+		DeviceInfo getCurrentDevice();
+		bool close();
+		
+		KeyboardModel getKeyboardModel();
 		
-		bool isAttached();
-		bool attach();
-		bool detach();
 		bool commit();
-		bool getKeyAddress(Key key, KeyAddress &keyAddress);
-		bool parsePowerOnEffect(std::string effect, PowerOnEffect &powerOnEffect);
-		bool parseKey(std::string key, KeyAddress &keyAddress);
-		bool parseKeyGroup(std::string key, KeyGroup &keyGroup);
-		bool parseColor(std::string color, KeyColors &colors);
-		bool setPowerOnEffect(PowerOnEffect powerOnEffect);
+		
 		bool setKey(KeyValue keyValue);
-		bool setKey(Key key, KeyColors colors);
-		bool setKeys(KeyValue keyValue[], size_t keyValueCount);
-		bool setAllKeys(KeyColors colors);
-		bool setGroupKeys(KeyGroup keyGroup, KeyColors colors);
+		bool setKeys(KeyValueArray keyValues);
+		bool setGroupKeys(KeyGroup keyGroup, Color color);
+		bool setAllKeys(Color color);
+		
+		bool setMRKey(uint8_t value);
+		bool setMNKey(uint8_t value);
+		bool setGKeysMode(uint8_t value);
+		
+		bool setRegion(uint8_t region, Color color);
+		bool setStartupMode(StartupMode startupMode);
+		
+		bool setNativeEffect(NativeEffect effect, NativeEffectPart part, uint8_t speed, Color color);
 		
 		
 	private:
 		
-		bool m_isAttached = false;
-		bool m_isKernellDetached = false;
-		KeyboardProtocol kbdProtocol = KeyboardProtocol::generic;
-		libusb_device **devs;
-		libusb_device_handle *dev_handle;
-		libusb_context *ctx = NULL;
+		typedef std::vector byte_buffer_t;
+		typedef std::vector KeyArray;
 		
-		bool populateAddressGroupInternal(KeyAddressGroup addressGroup, unsigned char *data);
-		bool sendDataInternal(unsigned char *data, uint16_t data_size);
-		bool setKeysInternal(KeyAddressGroup addressGroup, KeyValue keyValues[], size_t keyValueCount);
+		
+		const KeyArray keyGroupLogo = { Key::logo, Key::logo2 };
+		const KeyArray keyGroupIndicators = { Key::caps, Key::num, Key::scroll, Key::game, Key::backlight };
+		const KeyArray keyGroupMultimedia = { Key::next, Key::prev, Key::stop, Key::play, Key::mute };
+		const KeyArray keyGroupGKeys = { Key::g1, Key::g2, Key::g3, Key::g4, Key::g5, Key::g6, Key::g7, Key::g8, Key::g9 };
+		const KeyArray keyGroupFKeys = {
+			Key::f1, Key::f2, Key::f3, Key::f4, Key::f5, Key::f6, 
+			Key::f7, Key::f8, Key::f9, Key::f10, Key::f11, Key::f12 
+		};
+		const KeyArray keyGroupModifiers = {
+			Key::shift_left, Key::ctrl_left, Key::win_left, Key::alt_left, 
+			Key::alt_right, Key::win_right, Key::ctrl_right, Key::shift_right, Key::menu };
+		const KeyArray keyGroupFunctions = {
+			Key::esc, Key::print_screen, Key::scroll_lock, Key::pause_break,
+			Key::insert, Key::del, Key::home, Key::end, Key::page_up, Key::page_down
+		};
+		const KeyArray keyGroupArrows = { Key::arrow_top, Key::arrow_left, Key::arrow_bottom, Key::arrow_right };
+		const KeyArray keyGroupNumeric = {
+			Key::num_1, Key::num_2, Key::num_3, Key::num_4, Key::num_5,
+			Key::num_6, Key::num_7, Key::num_8, Key::num_9, Key::num_0, 
+			Key::num_dot, Key::num_enter, Key::num_plus, Key::num_minus,
+			Key::num_asterisk, Key::num_slash, Key::num_lock
+		};
+		const KeyArray keyGroupKeys = {
+			Key::a, Key::b, Key::c, Key::d, Key::e, Key::f, Key::g, Key::h, Key::i, Key::j, Key::k, Key::l, Key::m, 
+			Key::n, Key::o, Key::p, Key::q, Key::r, Key::s, Key::t, Key::u, Key::v, Key::w, Key::x, Key::y, Key::z,
+			Key::n1, Key::n2, Key::n3, Key::n4, Key::n5, Key::n6, Key::n7, Key::n8, Key::n9, Key::n0,
+			Key::enter, Key::backspace, Key::tab, Key::space, Key::minus, Key::equal,
+			Key::open_bracket, Key::close_bracket, Key::backslash, Key::dollar, Key::semicolon, Key::quote, Key::tilde,
+			Key::comma, Key::period, Key::slash, Key::caps_lock, Key::intl_backslash
+		};
+		
+		bool m_isOpen = false;
+		DeviceInfo currentDevice;
+		
+		#if defined(hidapi)
+			hid_device *m_hidHandle;
+		#elif defined(libusb)
+			bool m_isKernellDetached = false;
+			libusb_device_handle *m_hidHandle;
+			libusb_context *m_ctx = NULL;
+		#endif
+		
+		
+		bool sendDataInternal(byte_buffer_t &data);
+		byte_buffer_t getKeyGroupAddress(KeyAddressGroup keyAddressGroup);
 		
 };
 
diff --git a/src/helpers/help.cpp b/src/helpers/help.cpp
new file mode 100644
index 0000000..eb95f87
--- /dev/null
+++ b/src/helpers/help.cpp
@@ -0,0 +1,330 @@
+#include "help.h"
+
+#include 
+#include "utils.h"
+
+
+using namespace std;
+
+namespace help {
+	
+	inline KeyboardFeatures operator|(KeyboardFeatures a, KeyboardFeatures b) {
+		return static_cast(static_cast(a) | static_cast(b));
+	}
+	
+	KeyboardFeatures getKeyboardFeatures(string cmdName) {
+		if(cmdName == "g213-led") return KeyboardFeatures::g213;
+		else if(cmdName == "g410-led") return KeyboardFeatures::g410;
+		else if(cmdName == "g413-led") return KeyboardFeatures::g413;
+		else if(cmdName == "g610-led") return KeyboardFeatures::g610;
+		else if(cmdName == "g810-led") return KeyboardFeatures::g810;
+		else if(cmdName == "g910-led") return KeyboardFeatures::g910;
+		else if(cmdName == "gpro-led") return KeyboardFeatures::gpro;
+		return KeyboardFeatures::all;
+	}
+	
+	
+	void usage(char *arg0) {
+		string cmdName = utils::getCmdName(arg0);
+		KeyboardFeatures features = getKeyboardFeatures(cmdName);
+		cout<
+
+namespace help {
+	
+	enum class KeyboardFeatures : uint16_t {
+		none = 0,
+		rgb = 1,
+		intensity = 2,
+		commit = 4,
+		logo1 = 8,
+		logo2 = 16,
+		numpad = 32,
+		multimedia = 64,
+		gkeys = 128,
+		setall = 256,
+		setgroup = 512,
+		setkey = 1024,
+		setregion = 2048,
+		setindicators = 4096,
+		poweronfx = 8192,
+		// fx features
+		
+		all = rgb | intensity | commit | logo1 | logo2 | numpad | multimedia | gkeys |
+		      setall | setgroup | setkey | setregion | setindicators | poweronfx,
+		
+		g213 = rgb | logo1 | numpad | multimedia | setall | setregion | setindicators | poweronfx,
+		g410 = rgb | commit | setall | setgroup | setkey | poweronfx,
+		g413 = intensity | setall,
+		g610 = intensity | commit | logo1 | numpad | multimedia | setall | setgroup | setkey | setindicators | poweronfx,
+		g810 = rgb | commit | logo1 | numpad | multimedia | setall | setgroup | setkey | setindicators | poweronfx,
+		g910 = rgb | commit | logo1 | logo2 | numpad | multimedia | gkeys | setall | setgroup | setkey | setindicators | poweronfx,
+		gpro = rgb | commit | logo1 | multimedia | setall | setgroup | setkey | setindicators | poweronfx	
+	};
+	inline KeyboardFeatures operator|(KeyboardFeatures a, KeyboardFeatures b);
+	
+	KeyboardFeatures getKeyboardFeatures(std::string cmdName);
+	
+	void usage(char *arg0);
+	void keys(char *arg0);
+	void effects(char *arg0);
+	void samples(char *arg0);
+	
+}
+
+#ifndef VERSION
+#define VERSION "unspecified"
+#endif
+
+#endif
diff --git a/src/helpers/utils.cpp b/src/helpers/utils.cpp
new file mode 100644
index 0000000..5398838
--- /dev/null
+++ b/src/helpers/utils.cpp
@@ -0,0 +1,228 @@
+#include "utils.h"
+
+#include 
+#include 
+
+#include "../classes/Keyboard.h"
+
+
+namespace utils {
+	
+	std::string getCmdName(std::string cmd) {
+		return cmd.substr(cmd.find_last_of("/\\") + 1);
+	}
+	
+	
+	
+	bool parseStartupMode(std::string val, LedKeyboard::StartupMode &startupMode) {
+		if (val == "wave") startupMode = LedKeyboard::StartupMode::wave;
+		else if (val == "color") startupMode = LedKeyboard::StartupMode::color;
+		else return false;
+		return true;
+	}
+	
+	bool parseNativeEffect(std::string val, LedKeyboard::NativeEffect &nativeEffect) {
+		if (val == "color") nativeEffect = LedKeyboard::NativeEffect::color;
+		else if (val == "cycle") nativeEffect = LedKeyboard::NativeEffect::cycle;
+		else if (val == "breathing") nativeEffect = LedKeyboard::NativeEffect::breathing;
+		else if (val == "hwave") nativeEffect = LedKeyboard::NativeEffect::hwave;
+		else if (val == "vwave") nativeEffect = LedKeyboard::NativeEffect::vwave;
+		else if (val == "cwave") nativeEffect = LedKeyboard::NativeEffect::cwave;
+		else return false;
+		return true;
+	}
+	
+	bool parseNativeEffectPart(std::string val, LedKeyboard::NativeEffectPart &nativeEffectPart) {
+		if (val == "all") nativeEffectPart = LedKeyboard::NativeEffectPart::all;
+		else if (val == "keys") nativeEffectPart = LedKeyboard::NativeEffectPart::keys;
+		else if (val == "logo") nativeEffectPart = LedKeyboard::NativeEffectPart::logo;
+		else return false;
+		return true;
+	}
+	
+	bool parseKey(std::string val, LedKeyboard::Key &key) {
+		std::transform(val.begin(), val.end(), val.begin(), ::tolower);
+		if (val == "logo") key = LedKeyboard::Key::logo;
+		else if (val == "logo2") key = LedKeyboard::Key::logo2;
+		else if (val == "back_light" || val == "backlight" || val == "light") key = LedKeyboard::Key::backlight;
+		else if (val == "game_mode" || val == "gamemode" || val == "game") key = LedKeyboard::Key::game;
+		else if (val == "caps_indicator" || val == "capsindicator" || val == "caps") key = LedKeyboard::Key::caps;
+		else if (val == "scroll_indicator" || val == "scrollindicator" || val == "scroll") key = LedKeyboard::Key::scroll;
+		else if (val == "num_indicator" || val == "numindicator" || val == "num") key = LedKeyboard::Key::num;
+		else if (val == "next") key = LedKeyboard::Key::next;
+		else if (val == "prev" || val == "previous") key = LedKeyboard::Key::prev;
+		else if (val == "stop") key = LedKeyboard::Key::stop;
+		else if (val == "play_pause" || val == "playpause" || val == "play") key = LedKeyboard::Key::play;
+		else if (val == "mute") key = LedKeyboard::Key::mute;
+		else if (val == "a") key = LedKeyboard::Key::a;
+		else if (val == "b") key = LedKeyboard::Key::b;
+		else if (val == "c") key = LedKeyboard::Key::c;
+		else if (val == "d") key = LedKeyboard::Key::d;
+		else if (val == "e") key = LedKeyboard::Key::e;
+		else if (val == "f") key = LedKeyboard::Key::f;
+		else if (val == "g") key = LedKeyboard::Key::g;
+		else if (val == "h") key = LedKeyboard::Key::h;
+		else if (val == "i") key = LedKeyboard::Key::i;
+		else if (val == "j") key = LedKeyboard::Key::j;
+		else if (val == "k") key = LedKeyboard::Key::k;
+		else if (val == "l") key = LedKeyboard::Key::l;
+		else if (val == "m") key = LedKeyboard::Key::m;
+		else if (val == "n") key = LedKeyboard::Key::n;
+		else if (val == "o") key = LedKeyboard::Key::o;
+		else if (val == "p") key = LedKeyboard::Key::p;
+		else if (val == "q") key = LedKeyboard::Key::q;
+		else if (val == "r") key = LedKeyboard::Key::r;
+		else if (val == "s") key = LedKeyboard::Key::s;
+		else if (val == "t") key = LedKeyboard::Key::t;
+		else if (val == "u") key = LedKeyboard::Key::u;
+		else if (val == "v") key = LedKeyboard::Key::v;
+		else if (val == "w") key = LedKeyboard::Key::w;
+		else if (val == "x") key = LedKeyboard::Key::x;
+		else if (val == "z") key = LedKeyboard::Key::z;
+		else if (val == "y") key = LedKeyboard::Key::y;
+		else if (val == "1" || val == "one") key = LedKeyboard::Key::n1;
+		else if (val == "2" || val == "two") key = LedKeyboard::Key::n2;
+		else if (val == "3" || val == "three") key = LedKeyboard::Key::n3;
+		else if (val == "4" || val == "four") key = LedKeyboard::Key::n4;
+		else if (val == "5" || val == "five") key = LedKeyboard::Key::n5;
+		else if (val == "6" || val == "six") key = LedKeyboard::Key::n6;
+		else if (val == "7" || val == "seven") key = LedKeyboard::Key::n7;
+		else if (val == "8" || val == "eight") key = LedKeyboard::Key::n8;
+		else if (val == "9" || val == "nine") key = LedKeyboard::Key::n9;
+		else if (val == "0" || val == "zero") key = LedKeyboard::Key::n0;
+		else if (val == "enter") key = LedKeyboard::Key::enter;
+		else if (val == "esc" || val == "escape") key = LedKeyboard::Key::esc;
+		else if (val == "back" || val == "backspace") key = LedKeyboard::Key::backspace;
+		else if (val == "tab") key = LedKeyboard::Key::tab;
+		else if (val == "space") key = LedKeyboard::Key::space;
+		else if (val == "tilde" || val == "~") key = LedKeyboard::Key::tilde;
+		else if (val == "minus" || val == "-") key = LedKeyboard::Key::minus;
+		else if (val == "equal" || val == "=") key = LedKeyboard::Key::equal;
+		else if (val == "open_bracket" || val == "[") key = LedKeyboard::Key::open_bracket;
+		else if (val == "close_bracket" || val == "]") key = LedKeyboard::Key::close_bracket;
+		else if (val == "backslash" || val == "\\") key = LedKeyboard::Key::backslash;
+		else if (val == "semicolon" || val == ";") key = LedKeyboard::Key::semicolon;
+		else if (val == "quote" || val == "\"") key = LedKeyboard::Key::quote;
+		else if (val == "dollar" || val == "$") key = LedKeyboard::Key::dollar;
+		else if (val == "comma" || val == ",") key = LedKeyboard::Key::comma;
+		else if (val == "period" || val == ".") key = LedKeyboard::Key::period;
+		else if (val == "slash" || val == "/") key = LedKeyboard::Key::slash;
+		else if (val == "caps_lock" || val == "capslock") key = LedKeyboard::Key::caps_lock;
+		else if (val == "f1") key = LedKeyboard::Key::f1;
+		else if (val == "f2") key = LedKeyboard::Key::f2;
+		else if (val == "f3") key = LedKeyboard::Key::f3;
+		else if (val == "f4") key = LedKeyboard::Key::f4;
+		else if (val == "f5") key = LedKeyboard::Key::f5;
+		else if (val == "f6") key = LedKeyboard::Key::f6;
+		else if (val == "f7") key = LedKeyboard::Key::f7;
+		else if (val == "f8") key = LedKeyboard::Key::f8;
+		else if (val == "f9") key = LedKeyboard::Key::f9;
+		else if (val == "f10") key = LedKeyboard::Key::f10;
+		else if (val == "f11") key = LedKeyboard::Key::f11;
+		else if (val == "f12") key = LedKeyboard::Key::f12;
+		else if (val == "print_screen" || val == "printscreen" || val == "printscr" || val == "print")
+			key = LedKeyboard::Key::print_screen;
+		else if (val == "scroll_lock" || val == "scrolllock") key = LedKeyboard::Key::scroll_lock;
+		else if (val == "pause_break" || val == "pausebreak" || val == "pause" || val == "break")
+			key = LedKeyboard::Key::pause_break;
+		else if (val == "insert" || val == "ins") key = LedKeyboard::Key::insert;
+		else if (val == "home") key = LedKeyboard::Key::home;
+		else if (val == "page_up" || val == "pageup") key = LedKeyboard::Key::page_up;
+		else if (val == "delete" || val == "del") key = LedKeyboard::Key::del;
+		else if (val == "end") key = LedKeyboard::Key::end;
+		else if (val == "page_down" || val == "pagedown") key = LedKeyboard::Key::page_down;
+		else if (val == "arrow_right" || val == "arrowright" || val == "right") key = LedKeyboard::Key::arrow_right;
+		else if (val == "arrow_left" || val == "arrowleft" || val == "left") key = LedKeyboard::Key::arrow_left;
+		else if (val == "arrow_bottom" || val == "arrowbottom" || val == "bottom") key = LedKeyboard::Key::arrow_bottom;
+		else if (val == "arrow_top" || val == "arrowtop" || val == "top") key = LedKeyboard::Key::arrow_top;
+		else if (val == "num_lock" || val == "numlock") key = LedKeyboard::Key::num_lock;
+		else if (val == "num/" || val == "num_slash" || val == "numslash") key = LedKeyboard::Key::num_slash;
+		else if (val == "num*" || val == "num_asterisk" || val == "numasterisk") key = LedKeyboard::Key::num_asterisk;
+		else if (val == "num-" || val == "num_minus" || val == "numminus") key = LedKeyboard::Key::num_minus;
+		else if (val == "num+" || val == "num_plus" || val == "numplus") key = LedKeyboard::Key::num_plus;
+		else if (val == "numenter") key = LedKeyboard::Key::num_enter;
+		else if (val == "num1") key = LedKeyboard::Key::num_1;
+		else if (val == "num2") key = LedKeyboard::Key::num_2;
+		else if (val == "num3") key = LedKeyboard::Key::num_3;
+		else if (val == "num4") key = LedKeyboard::Key::num_4;
+		else if (val == "num5") key = LedKeyboard::Key::num_5;
+		else if (val == "num6") key = LedKeyboard::Key::num_6;
+		else if (val == "num7") key = LedKeyboard::Key::num_7;
+		else if (val == "num8") key = LedKeyboard::Key::num_8;
+		else if (val == "num9") key = LedKeyboard::Key::num_9;
+		else if (val == "num0") key = LedKeyboard::Key::num_0;
+		else if (val == "num." || val == "num_period" || val == "numperiod") key = LedKeyboard::Key::num_dot;
+		else if (val == "intl_backslash" || val == "<") key = LedKeyboard::Key::intl_backslash;
+		else if (val == "menu") key = LedKeyboard::Key::menu;
+		else if (val == "ctrl_left" || val == "ctrlleft" || val == "ctrll") key = LedKeyboard::Key::ctrl_left;
+		else if (val == "shift_left" || val == "shiftleft" || val == "shiftl") key = LedKeyboard::Key::shift_left;
+		else if (val == "alt_left" || val == "altleft" || val == "altl") key = LedKeyboard::Key::alt_left;
+		else if (val == "win_left" || val == "winleft" || val == "winl") key = LedKeyboard::Key::win_left;
+		else if (val == "meta_left" || val == "metaleft" || val == "metal") key = LedKeyboard::Key::win_left;
+		else if (val == "ctrl_right" || val == "ctrlright" || val == "ctrlr") key = LedKeyboard::Key::ctrl_right;
+		else if (val == "shift_right" || val == "shiftright" || val == "shiftr") key = LedKeyboard::Key::shift_right;
+		else if (val == "alt_right" || val == "altright" || val == "altr" || val == "altgr")
+			key = LedKeyboard::Key::alt_right;
+		else if (val == "win_right" || val == "winright" || val == "winr") key = LedKeyboard::Key::win_right;
+		else if (val == "meta_right" || val == "metaright" || val == "metar") key = LedKeyboard::Key::win_right;
+		else if (val == "g1") key = LedKeyboard::Key::g1;
+		else if (val == "g2") key = LedKeyboard::Key::g2;
+		else if (val == "g3") key = LedKeyboard::Key::g3;
+		else if (val == "g4") key = LedKeyboard::Key::g4;
+		else if (val == "g5") key = LedKeyboard::Key::g5;
+		else if (val == "g6") key = LedKeyboard::Key::g6;
+		else if (val == "g7") key = LedKeyboard::Key::g7;
+		else if (val == "g8") key = LedKeyboard::Key::g8;
+		else if (val == "g9") key = LedKeyboard::Key::g9;
+		else return false;
+		return true;
+	}
+	
+	bool parseKeyGroup(std::string val, LedKeyboard::KeyGroup &keyGroup) {
+		if (val == "logo") keyGroup = LedKeyboard::KeyGroup::logo;
+		else if (val == "indicators") keyGroup = LedKeyboard::KeyGroup::indicators;
+		else if (val == "multimedia") keyGroup = LedKeyboard::KeyGroup::multimedia;
+		else if (val == "fkeys") keyGroup = LedKeyboard::KeyGroup::fkeys;
+		else if (val == "modifiers") keyGroup = LedKeyboard::KeyGroup::modifiers;
+		else if (val == "arrows") keyGroup = LedKeyboard::KeyGroup::arrows;
+		else if (val == "numeric") keyGroup = LedKeyboard::KeyGroup::numeric;
+		else if (val == "functions") keyGroup = LedKeyboard::KeyGroup::functions;
+		else if (val == "keys") keyGroup = LedKeyboard::KeyGroup::keys;
+		else if (val == "gkeys") keyGroup = LedKeyboard::KeyGroup::gkeys;
+		else return false;
+		return true;
+	}
+	
+	bool parseColor(std::string val, LedKeyboard::Color &color) {
+		if (val.length() == 2) val = val + "0000";  // For G610
+		if (val.length() != 6) return false;
+		color.red = std::stoul("0x"+val.substr(0,2), nullptr, 16);
+		color.green = std::stoul("0x"+val.substr(2,2), nullptr, 16);
+		color.blue = std::stoul("0x"+val.substr(4,2), nullptr, 16);
+		return true;
+	}
+	
+	bool parseSpeed(std::string val, uint8_t &speed) {
+		if (val.length() == 1) val = "0" + val;
+		if (val.length() != 2) return false;
+		speed = std::stoul("0x" + val, nullptr, 16);
+		return true;
+	}
+	
+	bool parseUInt8(std::string val, uint8_t &uint8) {
+		if (val.length() == 1) val = "0" + val;
+		if (val.length() != 2) return false;
+		uint8 = std::stoul("0x" + val, nullptr, 16);
+		return true;
+	}
+	
+	bool parseUInt16(std::string val, uint16_t &uint16) {
+		if (val.length() == 1) val = "0" + val;
+		if (val.length() == 2) val = "0" + val;
+		if (val.length() == 3) val = "0" + val;
+		if (val.length() != 4) return false;
+		uint16 = std::stoul("0x" + val, nullptr, 16);
+		return true;
+	}
+	
+}
diff --git a/src/helpers/utils.h b/src/helpers/utils.h
new file mode 100644
index 0000000..92de26b
--- /dev/null
+++ b/src/helpers/utils.h
@@ -0,0 +1,23 @@
+#ifndef UTILS_HELPER
+#define UTILS_HELPER
+
+#include 
+#include "../classes/Keyboard.h"
+
+namespace utils {
+	
+	std::string getCmdName(std::string cmd);
+	
+	bool parseStartupMode(std::string val, LedKeyboard::StartupMode &startupMode);
+	bool parseNativeEffect(std::string val, LedKeyboard::NativeEffect &nativeEffect);
+	bool parseNativeEffectPart(std::string val, LedKeyboard::NativeEffectPart &nativeEffectPart);
+	bool parseKey(std::string val, LedKeyboard::Key &key);
+	bool parseKeyGroup(std::string val, LedKeyboard::KeyGroup &keyGroup);
+	bool parseColor(std::string val, LedKeyboard::Color &color);
+	bool parseSpeed(std::string val, uint8_t &speed);
+	bool parseUInt8(std::string val, uint8_t &uint8);
+	bool parseUInt16(std::string val, uint16_t &uint16);
+	
+}
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index b7af4eb..467589a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,310 +1,310 @@
-#include