diff --git a/SRGB_qmk_plugins/NuPhy_Air60v2_QMK_ANSI_Keyboard.js b/SRGB_qmk_plugins/NuPhy_Air60v2_QMK_ANSI_Keyboard.js
new file mode 100644
index 000000000000..c798a5c8ce12
--- /dev/null
+++ b/SRGB_qmk_plugins/NuPhy_Air60v2_QMK_ANSI_Keyboard.js
@@ -0,0 +1,331 @@
+export function Name() { return "NuPhy Air60 V2 QMK Keyboard"; }
+export function Version() { return "1.1.9"; }
+export function VendorId() { return 0x19f5; }
+export function ProductId() { return 0x3255; }
+export function Publisher() { return "ZZM"; }
+export function Documentation() { return "qmk/srgbmods-qmk-firmware"; }
+export function DeviceType() { return "keyboard"; }
+export function Size() { return [14, 5]; }
+export function DefaultPosition(){return [10, 100]; }
+export function DefaultScale(){return 8.0;}
+/* global
+shutdownMode:readonly
+shutdownColor:readonly
+LightingMode:readonly
+forcedColor:readonly
+*/
+export function ControllableParameters() {
+ return [
+ {"property":"shutdownMode", "group":"lighting", "label":"Shutdown Mode", "type":"combobox", "values":["SignalRGB", "Hardware"], "default":"SignalRGB"},
+ {"property":"shutdownColor", "group":"lighting", "label":"Shutdown Color", "min":"0", "max":"360", "type":"color", "default":"#000000"},
+ {"property":"LightingMode", "group":"lighting", "label":"Lighting Mode", "type":"combobox", "values":["Canvas", "Forced"], "default":"Canvas"},
+ {"property":"forcedColor", "group":"lighting", "label":"Forced Color", "min":"0", "max":"360", "type":"color", "default":"#009bde"},
+ ];
+}
+
+//Plugin Version: Built for Protocol V1.0.6
+
+const vKeys = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, //14
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, //14
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //13
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, //14
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, //9
+];
+
+const vKeyNames = [
+ "Esc", "!1", "@2", "#3", "$4", "%5", "^6", "&7", "*8", "(9", ")0", "_-", "+=", "Backspace", //14
+ "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\", //14
+ "CapsLock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter", //13
+ "Left Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "Right Shift", "Up Arrow", "Del", //14
+ "Left Ctrl", "Opt", "Cmd", "Space", "Right Cmd", "Fn", "Left Arrow", "Down Arrow", "Right Arrow", //9
+];
+
+const vKeyPositions = [
+ [0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0], [12, 0], [13, 0], //14
+ [0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1], [8, 1], [9, 1], [10, 1], [11, 1], [12, 1], [13, 1], //14
+ [0, 2], [1, 2], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2], [7, 2], [8, 2], [9, 2], [10, 2], [11, 2], [12, 2], //13
+ [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3], [6, 3], [7, 3], [8, 3], [9, 3], [10, 3], [11, 3], [12, 3], [13, 3], //14
+ [0, 4], [1, 4], [2, 4], [6, 4], [9, 4], [10, 4], [11, 4], [12, 4], [13, 4], //9
+];
+
+let LEDCount = 0;
+let IsViaKeyboard = false;
+const MainlineQMKFirmware = 1;
+const VIAFirmware = 2;
+const PluginProtocolVersion = "1.0.6";
+
+export function LedNames() {
+ return vKeyNames;
+}
+
+export function LedPositions() {
+ return vKeyPositions;
+}
+
+export function vKeysArrayCount() {
+ device.log('vKeys ' + vKeys.length);
+ device.log('vKeyNames ' + vKeyNames.length);
+ device.log('vKeyPositions ' + vKeyPositions.length);
+}
+
+export function Initialize() {
+ requestFirmwareType();
+ requestQMKVersion();
+ requestSignalRGBProtocolVersion();
+ requestUniqueIdentifier();
+ requestTotalLeds();
+ effectEnable();
+
+}
+
+export function Render() {
+ sendColors();
+}
+
+export function Shutdown(SystemSuspending) {
+
+ if(SystemSuspending) {
+ sendColors("#000000"); // Go Dark on System Sleep/Shutdown
+ } else {
+ if (shutdownMode === "SignalRGB") {
+ sendColors(shutdownColor);
+ } else {
+ effectDisable();
+ }
+ }
+
+ //vKeysArrayCount(); // For debugging array counts
+
+}
+
+function commandHandler() {
+ const readCounts = [];
+
+ do {
+ const returnpacket = device.read([0x00], 32, 10);
+ processCommands(returnpacket);
+
+ readCounts.push(device.getLastReadSize());
+
+ // Extra Read to throw away empty packets from Via
+ // Via always sends a second packet with the same Command Id.
+ if(IsViaKeyboard) {
+ device.read([0x00], 32, 10);
+ }
+ }
+ while(device.getLastReadSize() > 0);
+
+}
+
+function processCommands(data) {
+ switch(data[1]) {
+ case 0x21:
+ returnQMKVersion(data);
+ break;
+ case 0x22:
+ returnSignalRGBProtocolVersion(data);
+ break;
+ case 0x23:
+ returnUniqueIdentifier(data);
+ break;
+ case 0x24:
+ sendColors();
+ break;
+ case 0x27:
+ returnTotalLeds(data);
+ break;
+ case 0x28:
+ returnFirmwareType(data);
+ break;
+ }
+}
+
+function requestQMKVersion() //Check the version of QMK Firmware that the keyboard is running
+{
+ device.write([0x00, 0x21], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnQMKVersion(data) {
+ const QMKVersionByte1 = data[2];
+ const QMKVersionByte2 = data[3];
+ const QMKVersionByte3 = data[4];
+ device.log("QMK Version: " + QMKVersionByte1 + "." + QMKVersionByte2 + "." + QMKVersionByte3);
+ device.log("QMK SRGB Plugin Version: "+ Version());
+ device.pause(30);
+}
+
+function requestSignalRGBProtocolVersion() //Grab the version of the SignalRGB Protocol the keyboard is running
+{
+ device.write([0x00, 0x22], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnSignalRGBProtocolVersion(data) {
+ const ProtocolVersionByte1 = data[2];
+ const ProtocolVersionByte2 = data[3];
+ const ProtocolVersionByte3 = data[4];
+
+ const SignalRGBProtocolVersion = ProtocolVersionByte1 + "." + ProtocolVersionByte2 + "." + ProtocolVersionByte3;
+ device.log(`SignalRGB Protocol Version: ${SignalRGBProtocolVersion}`);
+
+
+ if(PluginProtocolVersion !== SignalRGBProtocolVersion) {
+ device.notify("Unsupported Protocol Version", `This plugin is intended for SignalRGB Protocol version ${PluginProtocolVersion}. This device is version: ${SignalRGBProtocolVersion}`, 2, "Documentation");
+ }
+
+ device.pause(30);
+}
+
+function requestUniqueIdentifier() //Grab the unique identifier for this keyboard model
+{
+ if(device.write([0x00, 0x23], 32) === -1) {
+ device.notify("Unsupported Firmware", "This device is not running SignalRGB-compatible firmware. Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ device.pause(30);
+ commandHandler();
+}
+
+
+function returnUniqueIdentifier(data) {
+ const UniqueIdentifierByte1 = data[2];
+ const UniqueIdentifierByte2 = data[3];
+ const UniqueIdentifierByte3 = data[4];
+
+ if(!(UniqueIdentifierByte1 === 0 && UniqueIdentifierByte2 === 0 && UniqueIdentifierByte3 === 0)) {
+ device.log("Unique Device Identifier: " + UniqueIdentifierByte1 + UniqueIdentifierByte2 + UniqueIdentifierByte3);
+ }
+
+ device.pause(30);
+}
+
+function requestTotalLeds() //Calculate total number of LEDs
+{
+ device.write([0x00, 0x27], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnTotalLeds(data) {
+ LEDCount = data[2];
+ device.log("Device Total LED Count: " + LEDCount);
+ device.pause(30);
+}
+
+function requestFirmwareType() {
+ device.write([0x00, 0x28], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnFirmwareType(data) {
+ const FirmwareTypeByte = data[2];
+
+ if(!(FirmwareTypeByte === MainlineQMKFirmware || FirmwareTypeByte === VIAFirmware)) {
+ device.notify("Unsupported Firmware", "Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ if(FirmwareTypeByte === MainlineQMKFirmware) {
+ IsViaKeyboard = false;
+ device.log("Firmware Type: Mainline");
+ }
+
+ if(FirmwareTypeByte === VIAFirmware) {
+ IsViaKeyboard = true;
+ device.log("Firmware Type: VIA");
+ }
+
+ device.pause(30);
+}
+
+function effectEnable() //Enable the SignalRGB Effect Mode
+{
+ device.write([0x00, 0x25], 32);
+ device.pause(30);
+}
+
+function effectDisable() //Revert to Hardware Mode
+{
+ device.write([0x00, 0x26], 32);
+ device.pause(30);
+}
+
+function createSolidColorArray(color) {
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function grabColors(overrideColor) {
+ if(overrideColor) {
+ return createSolidColorArray(hexToRgb(overrideColor));
+ } else if (LightingMode === "Forced") {
+ return createSolidColorArray(hexToRgb(forcedColor));
+ }
+
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iPxX = vKeyPositions[iIdx][0];
+ const iPxY = vKeyPositions[iIdx][1];
+ const color = device.color(iPxX, iPxY);
+
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function sendColors(overrideColor) {
+ const rgbdata = grabColors(overrideColor);
+
+ const LedsPerPacket = 9;
+ let BytesSent = 0;
+ let BytesLeft = rgbdata.length;
+
+ while(BytesLeft > 0) {
+ const BytesToSend = Math.min(LedsPerPacket * 3, BytesLeft);
+ StreamLightingData(Math.floor(BytesSent / 3), rgbdata.splice(0, BytesToSend));
+
+ BytesLeft -= BytesToSend;
+ BytesSent += BytesToSend;
+ }
+}
+
+function StreamLightingData(StartLedIdx, RGBData) {
+ const packet = [0x00, 0x24, StartLedIdx, Math.floor(RGBData.length / 3)].concat(RGBData);
+ device.write(packet, 33);
+}
+
+function hexToRgb(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ const colors = [];
+ colors[0] = parseInt(result[1], 16);
+ colors[1] = parseInt(result[2], 16);
+ colors[2] = parseInt(result[3], 16);
+
+ return colors;
+}
+
+export function Validate(endpoint) {
+ return endpoint.interface === 1;
+}
+
+export function ImageUrl()
+{
+ return "https://raw.githubusercontent.com/SRGBmods/QMK-Images/main/images/NuPhy_Air75v2.png";
+}
\ No newline at end of file
diff --git a/SRGB_qmk_plugins/NuPhy_Air75v2_QMK_ANSI_Keyboard.js b/SRGB_qmk_plugins/NuPhy_Air75v2_QMK_ANSI_Keyboard.js
new file mode 100644
index 000000000000..cf99befc6a83
--- /dev/null
+++ b/SRGB_qmk_plugins/NuPhy_Air75v2_QMK_ANSI_Keyboard.js
@@ -0,0 +1,334 @@
+export function Name() { return "NuPhy Air75 V2 QMK ANSI Keyboard"; }
+export function Version() { return "1.1.9"; }
+export function VendorId() { return 0x19f5; }
+export function ProductId() { return 0x3246; }
+export function Publisher() { return "ZZM"; }
+export function Documentation() { return "qmk/srgbmods-qmk-firmware"; }
+export function DeviceType() { return "keyboard"; }
+export function Size() { return [61, 6]; }
+export function DefaultPosition(){return [10, 100]; }
+export function DefaultScale(){return 8.0;}
+/* global
+shutdownMode:readonly
+shutdownColor:readonly
+LightingMode:readonly
+forcedColor:readonly
+*/
+export function ControllableParameters() {
+ return [
+ {"property":"shutdownMode", "group":"lighting", "label":"Shutdown Mode", "type":"combobox", "values":["SignalRGB", "Hardware"], "default":"SignalRGB"},
+ {"property":"shutdownColor", "group":"lighting", "label":"Shutdown Color", "min":"0", "max":"360", "type":"color", "default":"#000000"},
+ {"property":"LightingMode", "group":"lighting", "label":"Lighting Mode", "type":"combobox", "values":["Canvas", "Forced"], "default":"Canvas"},
+ {"property":"forcedColor", "group":"lighting", "label":"Forced Color", "min":"0", "max":"360", "type":"color", "default":"#009bde"},
+ ];
+}
+
+//Plugin Version: Built for Protocol V1.0.6
+
+const vKeys = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, //16
+ 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, //15
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, //15
+ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, //14
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, //14
+ 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, //9
+];
+
+const vKeyNames = [
+ "Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Print Screen", "Insert", "Del", //16
+ "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "Backspace", "Page Up", //15
+ "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\", "Page Down", //15
+ "CapsLock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter", "Home", //14
+ "Left Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "Right Shift", "Up Arrow", "End", //14
+ "Left Ctrl", "Left Win", "Left Alt", "Space", "Right Alt", "Fn", "Right Ctrl", "Left Arrow", "Down Arrow", "Right Arrow", //10
+];
+
+const vKeyPositions = [
+ [ 0, 0], [ 4, 0], [ 8, 0], [12, 0], [16, 0], [20, 0], [24, 0], [28, 0], [32, 0], [36, 0], [40, 0], [44, 0], [48, 0], [52, 0], [56, 0], [60, 0], //16
+ [ 0, 1], [ 4, 1], [ 8, 1], [12, 1], [16, 1], [20, 1], [24, 1], [28, 1], [32, 1], [36, 1], [40, 1], [44, 1], [48, 1], [54, 1], [60, 1], //15
+ [ 0, 2], [ 6, 2], [10, 2], [14, 2], [18, 2], [22, 2], [26, 2], [30, 2], [34, 2], [38, 2], [42, 2], [46, 2], [50, 2], [55, 2], [60, 2], //15
+ [ 0, 3], [ 7, 3], [11, 3], [15, 3], [19, 3], [23, 3], [27, 3], [31, 3], [35, 3], [39, 3], [43, 3], [47, 3], [53, 3], [60, 3], //14
+ [ 0, 4], [ 9, 4], [13, 4], [17, 4], [21, 4], [25, 4], [29, 4], [33, 4], [37, 4], [41, 4], [45, 4], [51, 4], [56, 4], [60, 4], //14
+ [ 0, 5], [ 5, 5], [ 10, 5], [26, 5], [40, 5], [44, 5], [48, 5], [52, 5], [56, 5], [60, 5], //10
+];
+
+let LEDCount = 0;
+let IsViaKeyboard = false;
+const MainlineQMKFirmware = 1;
+const VIAFirmware = 2;
+const PluginProtocolVersion = "1.0.6";
+
+export function LedNames() {
+ return vKeyNames;
+}
+
+export function LedPositions() {
+ return vKeyPositions;
+}
+
+export function vKeysArrayCount() {
+ device.log('vKeys ' + vKeys.length);
+ device.log('vKeyNames ' + vKeyNames.length);
+ device.log('vKeyPositions ' + vKeyPositions.length);
+}
+
+export function Initialize() {
+ requestFirmwareType();
+ requestQMKVersion();
+ requestSignalRGBProtocolVersion();
+ requestUniqueIdentifier();
+ requestTotalLeds();
+ effectEnable();
+
+}
+
+export function Render() {
+ sendColors();
+}
+
+export function Shutdown(SystemSuspending) {
+
+ if(SystemSuspending) {
+ sendColors("#000000"); // Go Dark on System Sleep/Shutdown
+ } else {
+ if (shutdownMode === "SignalRGB") {
+ sendColors(shutdownColor);
+ } else {
+ effectDisable();
+ }
+ }
+
+ //vKeysArrayCount(); // For debugging array counts
+
+}
+
+function commandHandler() {
+ const readCounts = [];
+
+ do {
+ const returnpacket = device.read([0x00], 32, 10);
+ processCommands(returnpacket);
+
+ readCounts.push(device.getLastReadSize());
+
+ // Extra Read to throw away empty packets from Via
+ // Via always sends a second packet with the same Command Id.
+ if(IsViaKeyboard) {
+ device.read([0x00], 32, 10);
+ }
+ }
+ while(device.getLastReadSize() > 0);
+
+}
+
+function processCommands(data) {
+ switch(data[1]) {
+ case 0x21:
+ returnQMKVersion(data);
+ break;
+ case 0x22:
+ returnSignalRGBProtocolVersion(data);
+ break;
+ case 0x23:
+ returnUniqueIdentifier(data);
+ break;
+ case 0x24:
+ sendColors();
+ break;
+ case 0x27:
+ returnTotalLeds(data);
+ break;
+ case 0x28:
+ returnFirmwareType(data);
+ break;
+ }
+}
+
+function requestQMKVersion() //Check the version of QMK Firmware that the keyboard is running
+{
+ device.write([0x00, 0x21], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnQMKVersion(data) {
+ const QMKVersionByte1 = data[2];
+ const QMKVersionByte2 = data[3];
+ const QMKVersionByte3 = data[4];
+ device.log("QMK Version: " + QMKVersionByte1 + "." + QMKVersionByte2 + "." + QMKVersionByte3);
+ device.log("QMK SRGB Plugin Version: "+ Version());
+ device.pause(30);
+}
+
+function requestSignalRGBProtocolVersion() //Grab the version of the SignalRGB Protocol the keyboard is running
+{
+ device.write([0x00, 0x22], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnSignalRGBProtocolVersion(data) {
+ const ProtocolVersionByte1 = data[2];
+ const ProtocolVersionByte2 = data[3];
+ const ProtocolVersionByte3 = data[4];
+
+ const SignalRGBProtocolVersion = ProtocolVersionByte1 + "." + ProtocolVersionByte2 + "." + ProtocolVersionByte3;
+ device.log(`SignalRGB Protocol Version: ${SignalRGBProtocolVersion}`);
+
+
+ if(PluginProtocolVersion !== SignalRGBProtocolVersion) {
+ device.notify("Unsupported Protocol Version", `This plugin is intended for SignalRGB Protocol version ${PluginProtocolVersion}. This device is version: ${SignalRGBProtocolVersion}`, 2, "Documentation");
+ }
+
+ device.pause(30);
+}
+
+function requestUniqueIdentifier() //Grab the unique identifier for this keyboard model
+{
+ if(device.write([0x00, 0x23], 32) === -1) {
+ device.notify("Unsupported Firmware", "This device is not running SignalRGB-compatible firmware. Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ device.pause(30);
+ commandHandler();
+}
+
+
+function returnUniqueIdentifier(data) {
+ const UniqueIdentifierByte1 = data[2];
+ const UniqueIdentifierByte2 = data[3];
+ const UniqueIdentifierByte3 = data[4];
+
+ if(!(UniqueIdentifierByte1 === 0 && UniqueIdentifierByte2 === 0 && UniqueIdentifierByte3 === 0)) {
+ device.log("Unique Device Identifier: " + UniqueIdentifierByte1 + UniqueIdentifierByte2 + UniqueIdentifierByte3);
+ }
+
+ device.pause(30);
+}
+
+function requestTotalLeds() //Calculate total number of LEDs
+{
+ device.write([0x00, 0x27], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnTotalLeds(data) {
+ LEDCount = data[2];
+ device.log("Device Total LED Count: " + LEDCount);
+ device.pause(30);
+}
+
+function requestFirmwareType() {
+ device.write([0x00, 0x28], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnFirmwareType(data) {
+ const FirmwareTypeByte = data[2];
+
+ if(!(FirmwareTypeByte === MainlineQMKFirmware || FirmwareTypeByte === VIAFirmware)) {
+ device.notify("Unsupported Firmware", "Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ if(FirmwareTypeByte === MainlineQMKFirmware) {
+ IsViaKeyboard = false;
+ device.log("Firmware Type: Mainline");
+ }
+
+ if(FirmwareTypeByte === VIAFirmware) {
+ IsViaKeyboard = true;
+ device.log("Firmware Type: VIA");
+ }
+
+ device.pause(30);
+}
+
+function effectEnable() //Enable the SignalRGB Effect Mode
+{
+ device.write([0x00, 0x25], 32);
+ device.pause(30);
+}
+
+function effectDisable() //Revert to Hardware Mode
+{
+ device.write([0x00, 0x26], 32);
+ device.pause(30);
+}
+
+function createSolidColorArray(color) {
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function grabColors(overrideColor) {
+ if(overrideColor) {
+ return createSolidColorArray(hexToRgb(overrideColor));
+ } else if (LightingMode === "Forced") {
+ return createSolidColorArray(hexToRgb(forcedColor));
+ }
+
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iPxX = vKeyPositions[iIdx][0];
+ const iPxY = vKeyPositions[iIdx][1];
+ const color = device.color(iPxX, iPxY);
+
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function sendColors(overrideColor) {
+ const rgbdata = grabColors(overrideColor);
+
+ const LedsPerPacket = 9;
+ let BytesSent = 0;
+ let BytesLeft = rgbdata.length;
+
+ while(BytesLeft > 0) {
+ const BytesToSend = Math.min(LedsPerPacket * 3, BytesLeft);
+ StreamLightingData(Math.floor(BytesSent / 3), rgbdata.splice(0, BytesToSend));
+
+ BytesLeft -= BytesToSend;
+ BytesSent += BytesToSend;
+ }
+}
+
+function StreamLightingData(StartLedIdx, RGBData) {
+ const packet = [0x00, 0x24, StartLedIdx, Math.floor(RGBData.length / 3)].concat(RGBData);
+ device.write(packet, 33);
+}
+
+function hexToRgb(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ const colors = [];
+ colors[0] = parseInt(result[1], 16);
+ colors[1] = parseInt(result[2], 16);
+ colors[2] = parseInt(result[3], 16);
+
+ return colors;
+}
+
+export function Validate(endpoint) {
+ return endpoint.interface === 1;
+}
+
+export function ImageUrl()
+{
+ return "https://raw.githubusercontent.com/SRGBmods/QMK-Images/main/images/NuPhy_Air75v2.png";
+}
\ No newline at end of file
diff --git a/SRGB_qmk_plugins/NuPhy_Air96v2_QMK_ANSI_Keyboard.js b/SRGB_qmk_plugins/NuPhy_Air96v2_QMK_ANSI_Keyboard.js
new file mode 100644
index 000000000000..22356c05c99c
--- /dev/null
+++ b/SRGB_qmk_plugins/NuPhy_Air96v2_QMK_ANSI_Keyboard.js
@@ -0,0 +1,329 @@
+export function Name() { return "NuPhy Air96 v2 ANSI QMK Keyboard"; }
+export function Version() { return "1.1.9"; }
+export function VendorId() { return 0x19F5; }
+export function ProductId() { return 0x3266; }
+export function Publisher() { return "ZZM"; }
+export function Documentation() { return "qmk/srgbmods-qmk-firmware"; }
+export function DeviceType() { return "keyboard"; }
+export function Size() { return [73, 11]; }
+export function DefaultPosition(){return [10, 100]; }
+export function DefaultScale(){return 8.0;}
+/* global
+shutdownMode:readonly
+shutdownColor:readonly
+LightingMode:readonly
+forcedColor:readonly
+*/
+export function ControllableParameters() {
+ return [
+ {"property":"shutdownMode", "group":"lighting", "label":"Shutdown Mode", "type":"combobox", "values":["SignalRGB", "Hardware"], "default":"SignalRGB"},
+ {"property":"shutdownColor", "group":"lighting", "label":"Shutdown Color", "min":"0", "max":"360", "type":"color", "default":"#000000"},
+ {"property":"LightingMode", "group":"lighting", "label":"Lighting Mode", "type":"combobox", "values":["Canvas", "Forced"], "default":"Canvas"},
+ {"property":"forcedColor", "group":"lighting", "label":"Forced Color", "min":"0", "max":"360", "type":"color", "default":"#009bde"},
+ ];
+}
+
+//Plugin Version: Built for Protocol V1.0.6
+
+const vKeys = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, //19
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, //18
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, //18
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, //16
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, //17
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 //12
+];
+
+const vKeyNames = [
+ "Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Print", "Del", "Home", "End", "Page Up", "Page Down", //19
+ "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "Backspace", "NumLock", "Num /", "Num *", "Num -", //18
+ "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "\\", "Num 7", "Num 8", "Num 9", "Num +", //18
+ "CapsLock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "Enter", "Num 4", "Num 5", "Num 6", //16
+ "Left Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "Right Shift", "Up Arrow", "Num 1", "Num 2", "Num 3", "Num Enter", //17
+ "Left Ctrl", "Left Win", "Left Alt", "Space", "Right Alt", "Fn", "LeftCtrl", "Left Arrow", "Down Arrow", "Right Arrow", "Num 0", "Num .", //12
+];
+
+const vKeyPositions = [
+ [0, 0], [4, 0], [8, 0], [12, 0], [16, 0], [20, 0], [24, 0], [28, 0], [32, 0], [36, 0], [40, 0], [44, 0], [48, 0], [52, 0], [56, 0], [60, 0], [64, 0], [68, 0], [72, 0], //19
+ [0, 2], [4, 2], [8, 2], [12, 2], [16, 2], [20, 2], [24, 2], [28, 2], [32, 2], [36, 2], [40, 2], [44, 2], [48, 2], [54, 2], [60, 2], [64, 2], [68, 2], [72, 2], //18
+ [0, 4], [6, 4], [10, 4], [14, 4], [18, 4], [22, 4], [26, 4], [30, 4], [34, 4], [38, 4], [42, 4], [46, 4], [50, 4], [55, 4], [60, 4], [64, 4], [68, 4], [72, 5], //18
+ [0, 6], [7, 6], [11, 6], [15, 6], [19, 6], [23, 6], [27, 6], [31, 6], [35, 6], [39, 6], [43, 6], [47, 6], [54, 6], [60, 6], [64, 6], [68, 6], //16
+ [0, 8], [9, 8], [13, 8], [17, 8], [21, 8], [25, 8], [29, 8], [33, 8], [37, 8], [41, 8], [45, 8], [51, 8], [56, 8], [60, 8], [64, 8], [68, 8], [72, 9], //17
+ [0,10], [5,10], [10,10], [26,10], [40,10], [44,10], [48,10], [52,10], [56,10], [60,10], [64,10], [68,10], //12
+];
+
+let LEDCount = 0;
+let IsViaKeyboard = false;
+const MainlineQMKFirmware = 1;
+const VIAFirmware = 2;
+const PluginProtocolVersion = "1.0.6";
+
+export function LedNames() {
+ return vKeyNames;
+}
+
+export function LedPositions() {
+ return vKeyPositions;
+}
+
+export function vKeysArrayCount() {
+ device.log('vKeys ' + vKeys.length);
+ device.log('vKeyNames ' + vKeyNames.length);
+ device.log('vKeyPositions ' + vKeyPositions.length);
+}
+
+export function Initialize() {
+ requestFirmwareType();
+ requestQMKVersion();
+ requestSignalRGBProtocolVersion();
+ requestUniqueIdentifier();
+ requestTotalLeds();
+ effectEnable();
+
+}
+
+export function Render() {
+ sendColors();
+}
+
+export function Shutdown(SystemSuspending) {
+
+ if(SystemSuspending) {
+ sendColors("#000000"); // Go Dark on System Sleep/Shutdown
+ } else {
+ if (shutdownMode === "SignalRGB") {
+ sendColors(shutdownColor);
+ } else {
+ effectDisable();
+ }
+ }
+
+ vKeysArrayCount(); // For debugging array counts
+
+}
+
+function commandHandler() {
+ const readCounts = [];
+
+ do {
+ const returnpacket = device.read([0x00], 32, 10);
+ processCommands(returnpacket);
+
+ readCounts.push(device.getLastReadSize());
+
+ // Extra Read to throw away empty packets from Via
+ // Via always sends a second packet with the same Command Id.
+ if(IsViaKeyboard) {
+ device.read([0x00], 32, 10);
+ }
+ }
+ while(device.getLastReadSize() > 0);
+
+}
+
+function processCommands(data) {
+ switch(data[1]) {
+ case 0x21:
+ returnQMKVersion(data);
+ break;
+ case 0x22:
+ returnSignalRGBProtocolVersion(data);
+ break;
+ case 0x23:
+ returnUniqueIdentifier(data);
+ break;
+ case 0x24:
+ sendColors();
+ break;
+ case 0x27:
+ returnTotalLeds(data);
+ break;
+ case 0x28:
+ returnFirmwareType(data);
+ break;
+ }
+}
+
+function requestQMKVersion() //Check the version of QMK Firmware that the keyboard is running
+{
+ device.write([0x00, 0x21], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnQMKVersion(data) {
+ const QMKVersionByte1 = data[2];
+ const QMKVersionByte2 = data[3];
+ const QMKVersionByte3 = data[4];
+ device.log("QMK Version: " + QMKVersionByte1 + "." + QMKVersionByte2 + "." + QMKVersionByte3);
+ device.log("QMK SRGB Plugin Version: "+ Version());
+ device.pause(30);
+}
+
+function requestSignalRGBProtocolVersion() //Grab the version of the SignalRGB Protocol the keyboard is running
+{
+ device.write([0x00, 0x22], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnSignalRGBProtocolVersion(data) {
+ const ProtocolVersionByte1 = data[2];
+ const ProtocolVersionByte2 = data[3];
+ const ProtocolVersionByte3 = data[4];
+
+ const SignalRGBProtocolVersion = ProtocolVersionByte1 + "." + ProtocolVersionByte2 + "." + ProtocolVersionByte3;
+ device.log(`SignalRGB Protocol Version: ${SignalRGBProtocolVersion}`);
+
+
+ if(PluginProtocolVersion !== SignalRGBProtocolVersion) {
+ device.notify("Unsupported Protocol Version", `This plugin is intended for SignalRGB Protocol version ${PluginProtocolVersion}. This device is version: ${SignalRGBProtocolVersion}`, 2, "Documentation");
+ }
+
+ device.pause(30);
+}
+
+function requestUniqueIdentifier() //Grab the unique identifier for this keyboard model
+{
+ if(device.write([0x00, 0x23], 32) === -1) {
+ device.notify("Unsupported Firmware", "This device is not running SignalRGB-compatible firmware. Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ device.pause(30);
+ commandHandler();
+}
+
+
+function returnUniqueIdentifier(data) {
+ const UniqueIdentifierByte1 = data[2];
+ const UniqueIdentifierByte2 = data[3];
+ const UniqueIdentifierByte3 = data[4];
+
+ if(!(UniqueIdentifierByte1 === 0 && UniqueIdentifierByte2 === 0 && UniqueIdentifierByte3 === 0)) {
+ device.log("Unique Device Identifier: " + UniqueIdentifierByte1 + UniqueIdentifierByte2 + UniqueIdentifierByte3);
+ }
+
+ device.pause(30);
+}
+
+function requestTotalLeds() //Calculate total number of LEDs
+{
+ device.write([0x00, 0x27], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnTotalLeds(data) {
+ LEDCount = data[2];
+ device.log("Device Total LED Count: " + LEDCount);
+ device.pause(30);
+}
+
+function requestFirmwareType() {
+ device.write([0x00, 0x28], 32);
+ device.pause(30);
+ commandHandler();
+}
+
+function returnFirmwareType(data) {
+ const FirmwareTypeByte = data[2];
+
+ if(!(FirmwareTypeByte === MainlineQMKFirmware || FirmwareTypeByte === VIAFirmware)) {
+ device.notify("Unsupported Firmware", "Click the Documentation button to learn more.", 3, "Documentation");
+ }
+
+ if(FirmwareTypeByte === MainlineQMKFirmware) {
+ IsViaKeyboard = false;
+ device.log("Firmware Type: Mainline");
+ }
+
+ if(FirmwareTypeByte === VIAFirmware) {
+ IsViaKeyboard = true;
+ device.log("Firmware Type: VIA");
+ }
+
+ device.pause(30);
+}
+
+function effectEnable() //Enable the SignalRGB Effect Mode
+{
+ device.write([0x00, 0x25], 32);
+ device.pause(30);
+}
+
+function effectDisable() //Revert to Hardware Mode
+{
+ device.write([0x00, 0x26], 32);
+ device.pause(30);
+}
+
+function createSolidColorArray(color) {
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function grabColors(overrideColor) {
+ if(overrideColor) {
+ return createSolidColorArray(hexToRgb(overrideColor));
+ } else if (LightingMode === "Forced") {
+ return createSolidColorArray(hexToRgb(forcedColor));
+ }
+
+ const rgbdata = new Array(vKeys.length * 3).fill(0);
+
+ for(let iIdx = 0; iIdx < vKeys.length; iIdx++) {
+ const iPxX = vKeyPositions[iIdx][0];
+ const iPxY = vKeyPositions[iIdx][1];
+ const color = device.color(iPxX, iPxY);
+
+ const iLedIdx = vKeys[iIdx] * 3;
+ rgbdata[iLedIdx] = color[0];
+ rgbdata[iLedIdx+1] = color[1];
+ rgbdata[iLedIdx+2] = color[2];
+ }
+
+ return rgbdata;
+}
+
+function sendColors(overrideColor) {
+ const rgbdata = grabColors(overrideColor);
+
+ const LedsPerPacket = 9;
+ let BytesSent = 0;
+ let BytesLeft = rgbdata.length;
+
+ while(BytesLeft > 0) {
+ const BytesToSend = Math.min(LedsPerPacket * 3, BytesLeft);
+ StreamLightingData(Math.floor(BytesSent / 3), rgbdata.splice(0, BytesToSend));
+
+ BytesLeft -= BytesToSend;
+ BytesSent += BytesToSend;
+ }
+}
+
+function StreamLightingData(StartLedIdx, RGBData) {
+ const packet = [0x00, 0x24, StartLedIdx, Math.floor(RGBData.length / 3)].concat(RGBData);
+ device.write(packet, 33);
+}
+
+function hexToRgb(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ const colors = [];
+ colors[0] = parseInt(result[1], 16);
+ colors[1] = parseInt(result[2], 16);
+ colors[2] = parseInt(result[3], 16);
+
+ return colors;
+}
+
+export function Validate(endpoint) {
+ return endpoint.interface === 1;
+}
\ No newline at end of file
diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index 3a6f6f6579e1..26a9b1721bb5 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -638,6 +638,12 @@ ifeq ($(strip $(VIA_ENABLE)), yes)
TRI_LAYER_ENABLE := yes
endif
+ifeq ($(strip $(SIGNALRGB_ENABLE)), yes)
+ RAW_ENABLE := yes
+ SRC += $(QUANTUM_DIR)/signalrgb.c
+ OPT_DEFS += -DSIGNALRGB_ENABLE
+endif
+
VALID_CUSTOM_MATRIX_TYPES:= yes lite no
CUSTOM_MATRIX ?= no
diff --git a/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/NuPhy Air96 V2 via3.json b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/NuPhy Air96 V2 via3.json
new file mode 100644
index 000000000000..2a7e31de21bb
--- /dev/null
+++ b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/NuPhy Air96 V2 via3.json
@@ -0,0 +1,195 @@
+{
+ "name": "NuPhy Air96 V2",
+ "vendorId": "0x19F5",
+ "productId": "0x3266",
+ "matrix": {
+ "rows": 6,
+ "cols": 21
+ },
+ "layouts": {
+ "keymap": [
+ ["0,0","0,2","0,3","0,4","0,5","0,6","0,7","0,8","0,9","0,10","0,11","0,12","0,13","0,14","2,14","1,15","2,15","1,16","2,16"],
+ ["1,0","1,1","1,2","1,3","1,4","1,5","1,6","1,7","1,8","1,9","1,10","1,11","1,12",{"w":2},"1,13","1,17","1,18","1,19","1,20"],
+ [{"w":1.5},"2,0","2,1","2,2","2,3","2,4","2,5","2,6","2,7","2,8","2,9","2,10","2,11","2,12",{"w":1.5},"2,13","2,17","2,18","2,19",{"h":2},"2,20"],
+ [{"w": 1.75},"3,0","3,1","3,2","3,3","3,4","3,5","3,6","3,7","3,8","3,9","3,10","3,11",{"w":2.25},"3,13","3,17","3,18","3,19"],
+ [{"w":2.25},"4,0","4,2","4,3","4,4","4,5","4,6","4,7","4,8","4,9","4,10","4,11",{"w": 1.75},"4,13","4,15","4,17","4,18","4,19",{"h":2},"4,20"],
+ [{"w":1.25},"5,0",{"w":1.25},"5,1",{"w":1.25},"5,2",{"w":6.25},"5,6","5,9","5,10","5,13","5,14","5,15","5,16","5,17","5,19"]
+ ]
+ },
+ "menus": [
+ {
+ "label": "Lighting",
+ "content": [
+ {
+ "label": "Backlight",
+ "content": [
+ {
+ "label": "Brightness",
+ "type": "range",
+ "options": [0, 255],
+ "content": ["id_qmk_rgb_matrix_brightness", 3, 1]
+ },
+ {
+ "label": "Effect",
+ "type": "dropdown",
+ "content": ["id_qmk_rgb_matrix_effect", 3, 2],
+ "options": [
+ "All Off",
+ "Solid Color",
+ "Gradient Up/Down",
+ "Gradient Left/Right",
+ "Breathing",
+ "Band Sat.",
+ "Band Val.",
+ "Pinwheel Sat.",
+ "Pinwheel Val.",
+ "Spiral Sat.",
+ "Spiral Val.",
+ "Cycle All",
+ "Cycle Left/Right",
+ "Cycle Up/Down",
+ "Rainbow Moving Chevron",
+ "Cycle Out/In",
+ "Cycle Out/In Dual",
+ "Cycle Pinwheel",
+ "Cycle Spiral",
+ "Dual Beacon",
+ "Rainbow Beacon",
+ "Rainbow Pinwheels",
+ "Raindrops",
+ "Jellybean Raindrops",
+ "Hue Breathing",
+ "Hue Pendulum",
+ "Hue Wave",
+ "Typing Heatmap",
+ "Digital Rain",
+ "Reactive Simple",
+ "Reactive",
+ "Reactive Wide",
+ "Reactive Multiwide",
+ "Reactive Cross",
+ "Reactive Multicross",
+ "Reactive Nexus",
+ "Reactive MultiNexus",
+ "Splash",
+ "MultiSplash",
+ "Solid Splash",
+ "Solid MultiSplash"
+ ]
+ },
+ {
+ "showIf": "{id_qmk_rgb_matrix_effect} != 0",
+ "label": "Effect Speed",
+ "type": "range",
+ "options": [0, 255],
+ "content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
+ },
+ {
+ "showIf": "{id_qmk_rgb_matrix_effect} != 0",
+ "label": "Color",
+ "type": "color",
+ "content": ["id_qmk_rgb_matrix_color", 3, 4]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+"keycodes": [
+ "qmk_lighting"
+],
+"customKeycodes": [
+ {
+ "name": "RF\nDFU",
+ "title": "RF DFU"
+ },
+ {
+ "name": "Link\nUSB",
+ "title": "Link USB"
+ },
+ {
+ "name": "Link\nRF",
+ "title": "Link RF"
+ },
+ {
+ "name": "Link\nBLE_1",
+ "title": "Link BLE_1"
+ },
+ {
+ "name": "Link\nBLE_2",
+ "title": "Link BLE_2"
+ },
+ {
+ "name": "Link\nBLE_3",
+ "title": "Link BLE_3"
+ },
+ {
+ "name": "Mac\nTask",
+ "title": "Mac Task"
+ },
+ {
+ "name": "Mac\nSearch",
+ "title": "Mac Search"
+ },
+ {
+ "name": "Mac\nVoice",
+ "title": "Mac Siri Voice"
+ },
+ {
+ "name": "Mac\nConsole",
+ "title": "Mac Console"
+ },
+ {
+ "name": "Mac\nDnt",
+ "title": "Mac Dnt"
+ },
+ {
+ "name": "Print\n Whole",
+ "title": "Print Whole"
+ },
+ {
+ "name": "Print\n Area",
+ "title": "Print Area"
+ },
+ {
+ "name": "Side\nLight+",
+ "title": "Side Light +"
+ },
+ {
+ "name": "Side\nLight-",
+ "title": "Side Light -"
+ },
+ {
+ "name": "Side\nMode",
+ "title": "Side Next Mode"
+ },
+ {
+ "name": "Side\nColor",
+ "title": "Side Next Color"
+ },
+ {
+ "name": "Side\nFast",
+ "title": "Side Speed +"
+ },
+ {
+ "name": "Side\nSlow",
+ "title": "Side Speed -"
+ },
+ {
+ "name": "Dev\nReset",
+ "title": "Device Reset"
+ },
+ {
+ "name": "Sleep\nMode",
+ "title": "Auto Sleep Mode"
+ },
+ {
+ "name": "Bat\nShow",
+ "title": "Battery Show"
+ },
+ {
+ "name": "RGB\nTest",
+ "title": "Hold for 3 seconds for RGB color test"
+ }
+]
+}
diff --git a/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/keymap.c b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/keymap.c
new file mode 100644
index 000000000000..fe3dbc5c3729
--- /dev/null
+++ b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/keymap.c
@@ -0,0 +1,178 @@
+/*
+Copyright 2023 @ Nuphy
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include QMK_KEYBOARD_H
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
+// layer 0 Mac
+[0] = LAYOUT(
+ KC_ESC, KC_BRID, KC_BRIU, MAC_TASK, MAC_SEARCH, MAC_VOICE, MAC_DND, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, MAC_PRTA, KC_DEL, KC_HOME, KC_END, KC_PGUP, KC_PGDN,
+ KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
+ KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_P7, KC_P8, KC_P9, KC_PPLS,
+ KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6,
+ KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3, KC_PENT,
+ KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RGUI, MO(1), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT),
+// layer 1 Mac Fn
+[1] = LAYOUT(
+ _______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, MAC_PRT, KC_INS, _______, _______, _______, _______,
+ _______, LNK_BLE1, LNK_BLE2, LNK_BLE3, LNK_RF, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, DEV_RESET, SLEEP_MODE, BAT_SHOW, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ MO(4), _______, _______, _______, _______, _______, _______, MO(4), RGB_SPD, RGB_SPI, _______, MO(4), RGB_VAI, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, RGB_MOD, RGB_VAD, RGB_HUI, _______, _______),
+// layer 2 win
+[2] = LAYOUT(
+ KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_DEL, KC_HOME, KC_END, KC_PGUP, KC_PGDN,
+ KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
+ KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_P7, KC_P8, KC_P9, KC_PPLS,
+ KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6,
+ KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3, KC_PENT,
+ KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(3), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT),
+// layer 3 win Fn
+[3] = LAYOUT(
+ _______, KC_BRID, KC_BRIU, _______, _______, _______, _______, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, MAC_PRTA, KC_INS, _______, _______, _______, _______,
+ _______, LNK_BLE1, LNK_BLE2, LNK_BLE3, LNK_RF, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, DEV_RESET, SLEEP_MODE, BAT_SHOW, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ MO(4), _______, _______, _______, _______, _______, _______, MO(4), RGB_SPD, RGB_SPI, _______, MO(4), RGB_VAI, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, RGB_MOD, RGB_VAD, RGB_HUI, _______, _______),
+// layer 4 function
+[4] = LAYOUT(
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
+ _______, _______, _______, RGB_TEST, _______, _______, _______, _______, SIDE_SPD, SIDE_SPI, _______, _______, SIDE_VAI, _______, _______, _______, _______,
+ _______, _______, _______, _______, _______, MO(4), _______, SIDE_MOD, SIDE_VAD, SIDE_HUI, _______, _______)
+};
+
+
+const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
+ {0, A_16, B_16, C_16},
+ {0, A_15, B_15, C_15},
+ {0, A_14, B_14, C_14},
+ {0, A_13, B_13, C_13},
+ {0, A_12, B_12, C_12},
+ {0, D_16, E_16, F_16},
+ {0, D_15, E_15, F_15},
+ {0, D_14, E_14, F_14},
+ {0, D_13, E_13, F_13},
+ {0, D_12, E_12, F_12},
+ {1, D_16, E_16, F_16},
+ {1, D_15, E_15, F_15},
+ {1, D_14, E_14, F_14},
+ {1, D_13, E_13, F_13},
+ {1, G_4, H_4, I_4},
+ {1, D_5, E_5, F_5},
+ {1, G_5, H_5, I_5},
+ {1, D_6, E_6, F_6},
+ {1, G_6, H_6, I_6},
+ {0, A_1, B_1, C_1},
+ {0, A_2, B_2, C_2},
+ {0, A_3, B_3, C_3},
+ {0, A_4, B_4, C_4},
+ {0, A_5, B_5, C_5},
+ {0, A_6, B_6, C_6},
+ {0, A_7, B_7, C_7},
+ {0, A_8, B_8, C_8},
+ {0, A_9, B_9, C_9},
+ {0, A_10, B_10, C_10},
+ {0, A_11, B_11, C_11},
+ {1, D_1, E_1, F_1},
+ {1, D_2, E_2, F_2},
+ {1, D_3, E_3, F_3},
+ {1, D_7, E_7, F_7},
+ {1, D_8, E_8, F_8},
+ {1, D_9, E_9, F_9},
+ {1, D_10, E_10, F_10},
+ {0, D_1, E_1, F_1},
+ {0, D_2, E_2, F_2},
+ {0, D_3, E_3, F_3},
+ {0, D_4, E_4, F_4},
+ {0, D_5, E_5, F_5},
+ {0, D_6, E_6, F_6},
+ {0, D_7, E_7, F_7},
+ {0, D_8, E_8, F_8},
+ {0, D_9, E_9, F_9},
+ {0, D_10, E_10, F_10},
+ {0, D_11, E_11, F_11},
+ {1, G_1, H_1, I_1},
+ {1, G_2, H_2, I_2},
+ {1, G_3, H_3, I_3},
+ {1, G_7, H_7, I_7},
+ {1, G_8, H_8, I_8},
+ {1, G_9, H_9, I_9},
+ {1, G_10, H_10, I_10},
+ {0, G_1, H_1, I_1},
+ {0, G_2, H_2, I_2},
+ {0, G_3, H_3, I_3},
+ {0, G_4, H_4, I_4},
+ {0, G_5, H_5, I_5},
+ {0, G_6, H_6, I_6},
+ {0, G_7, H_7, I_7},
+ {0, G_8, H_8, I_8},
+ {0, G_9, H_9, I_9},
+ {0, G_10, H_10, I_10},
+ {0, G_11, H_11, I_11},
+ {1, G_16, H_16, I_16},
+ {1, G_14, H_14, I_14},
+ {1, G_13, H_13, I_13},
+ {1, G_12, H_12, I_12},
+ {1, G_11, H_11, I_11},
+ {0, J_1, K_1, L_1},
+ {0, J_3, K_3, L_3},
+ {0, J_4, K_4, L_4},
+ {0, J_5, K_5, L_5},
+ {0, J_6, K_6, L_6},
+ {0, J_7, K_7, L_7},
+ {0, J_8, K_8, L_8},
+ {0, J_9, K_9, L_9},
+ {0, J_10, K_10, L_10},
+ {0, J_11, K_11, L_11},
+ {1, J_1, K_1, L_1},
+ {1, J_3, K_3, L_3},
+ {1, J_4, K_4, L_4},
+ {1, J_5, K_5, L_5},
+ {1, J_6, K_6, L_6},
+ {1, J_7, K_7, L_7},
+ {1, J_8, K_8, L_8},
+ {0, J_16, K_16, L_16},
+ {0, J_15, K_15, L_15},
+ {0, J_14, K_14, L_14},
+ {0, J_13, K_13, L_13},
+ {0, J_12, K_12, L_12},
+ {1, J_16, K_16, L_16},
+ {1, J_14, K_14, L_14},
+ {1, J_13, K_13, L_13},
+ {1, J_12, K_12, L_12},
+ {1, J_11, K_11, L_11},
+ {1, J_10, K_10, L_10},
+ {1, J_9, K_9, L_9},
+
+ {1, A_5, B_5, C_5},
+ {1, A_4, B_4, C_4},
+ {1, A_3, B_3, C_3},
+ {1, A_2, B_2, C_2},
+ {1, A_1, B_1, C_1},
+
+ {1, A_6, B_6, C_6},
+ {1, A_7, B_7, C_7},
+ {1, A_8, B_8, C_8},
+ {1, A_9, B_9, C_9},
+ {1, A_10, B_10, C_10}
+};
diff --git a/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/rules.mk b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/rules.mk
new file mode 100644
index 000000000000..5d69e14570ec
--- /dev/null
+++ b/keyboards/nuphy/air96_v2/ansi/keymaps/srgb/rules.mk
@@ -0,0 +1,2 @@
+VIA_ENABLE = yes
+SIGNALRGB_ENABLE = yes
diff --git a/keyboards/nuphy/air96_v2/ansi/side.c b/keyboards/nuphy/air96_v2/ansi/side.c
index 9bc667555392..3b0941d15a09 100644
--- a/keyboards/nuphy/air96_v2/ansi/side.c
+++ b/keyboards/nuphy/air96_v2/ansi/side.c
@@ -301,6 +301,9 @@ void sys_led_show(void)
if (host_keyboard_led_state().caps_lock) {
set_left_rgb(0X00, SIDE_BLINK_LIGHT, SIDE_BLINK_LIGHT);
}
+ if (!host_keyboard_led_state().num_lock) {
+ set_right_rgb(0X00, SIDE_BLINK_LIGHT, SIDE_BLINK_LIGHT);
+ }
}
else {
if (dev_info.rf_led & 0x02) {
@@ -635,10 +638,10 @@ void bat_percent_led(uint8_t bat_percent)
bat_r = SIDE_BLINK_LIGHT, bat_g = SIDE_BLINK_LIGHT / 2, bat_b = 0;
} else if (bat_percent <= 70) {
bat_end_led = 3;
- bat_r = SIDE_BLINK_LIGHT, bat_g = SIDE_BLINK_LIGHT / 2, bat_b = 0;
+ bat_r = SIDE_BLINK_LIGHT, bat_g = SIDE_BLINK_LIGHT, bat_b = 0;
} else if (bat_percent <= 95) {
bat_end_led = 4;
- bat_r = SIDE_BLINK_LIGHT, bat_g = SIDE_BLINK_LIGHT / 2, bat_b = 0;
+ bat_r = SIDE_BLINK_LIGHT, bat_g = SIDE_BLINK_LIGHT, bat_b = 0;
} else {
bat_end_led = 4;
bat_r = 0, bat_g = SIDE_BLINK_LIGHT, bat_b = 0;
diff --git a/quantum/qmk_version.h b/quantum/qmk_version.h
new file mode 100644
index 000000000000..c420733791a8
--- /dev/null
+++ b/quantum/qmk_version.h
@@ -0,0 +1,3 @@
+#define QMK_VERSION_BYTE_1 0
+#define QMK_VERSION_BYTE_2 26
+#define QMK_VERSION_BYTE_3 0
diff --git a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
index a02238a2d1a6..e2d2e6b67923 100644
--- a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
+++ b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
@@ -42,4 +42,7 @@
#include "starlight_anim.h"
#include "starlight_dual_sat_anim.h"
#include "starlight_dual_hue_anim.h"
-#include "riverflow_anim.h"
\ No newline at end of file
+#include "riverflow_anim.h"
+#ifdef SIGNALRGB_ENABLE
+ #include "signalrgb_anim.h"
+#endif
\ No newline at end of file
diff --git a/quantum/rgb_matrix/animations/signalrgb_anim.h b/quantum/rgb_matrix/animations/signalrgb_anim.h
new file mode 100644
index 000000000000..39ce5129b2a5
--- /dev/null
+++ b/quantum/rgb_matrix/animations/signalrgb_anim.h
@@ -0,0 +1,12 @@
+// animations/signalrgb_anim.h
+RGB_MATRIX_EFFECT(SIGNALRGB)
+#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+
+bool SIGNALRGB(effect_params_t* params)
+{
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+
+#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS
\ No newline at end of file
diff --git a/quantum/signalrgb.c b/quantum/signalrgb.c
new file mode 100644
index 000000000000..3b3a71da8eb4
--- /dev/null
+++ b/quantum/signalrgb.c
@@ -0,0 +1,193 @@
+#ifndef RAW_ENABLE
+# error "RAW HID Communication is not enabled" //This should be impossible to run into afaik. Common_features ensures RAWHID is enabled.
+#endif
+
+#include "qmk_version.h"
+#include "protocol/host.h"
+#include "version.h"
+#include "quantum.h"
+#include "signalrgb.h"
+#include "color.h"
+#include "raw_hid.h"
+
+
+ uint8_t packet[32];
+
+void get_qmk_version(void) //Grab the QMK Version the board's firmware is built off of
+{
+ packet[0] = GET_QMK_VERSION;
+ packet[1] = QMK_VERSION_BYTE_1;
+ packet[2] = QMK_VERSION_BYTE_2;
+ packet[3] = QMK_VERSION_BYTE_3;
+
+ raw_hid_send(packet, 32);
+}
+
+void get_signalrgb_protocol_version(void)
+{
+ packet[0] = GET_PROTOCOL_VERSION;
+ packet[1] = PROTOCOL_VERSION_BYTE_1;
+ packet[2] = PROTOCOL_VERSION_BYTE_2;
+ packet[3] = PROTOCOL_VERSION_BYTE_3;
+
+ raw_hid_send(packet, 32);
+}
+
+void get_unique_identifier(void) //Grab the unique identifier for each specific model of keyboard.
+{
+ packet[0] = GET_UNIQUE_IDENTIFIER;
+ packet[1] = DEVICE_UNIQUE_IDENTIFIER_BYTE_1;
+ packet[2] = DEVICE_UNIQUE_IDENTIFIER_BYTE_2;
+ packet[3] = DEVICE_UNIQUE_IDENTIFIER_BYTE_3;
+
+ raw_hid_send(packet, 32);
+}
+
+void led_streaming(uint8_t *data) //Stream data from HID Packets to Keyboard.
+{
+ uint8_t index = data[1];
+ uint8_t numberofleds = data[2];
+ #if defined(RGBLIGHT_ENABLE)
+ if(index + numberofleds > RGBLIGHT_LED_COUNT) {
+ #elif defined(RGB_MATRIX_ENABLE)
+ if(index + numberofleds > RGB_MATRIX_LED_COUNT) {
+ #endif
+ packet[1] = DEVICE_ERROR_LED_BOUNDS;
+ raw_hid_send(packet,32);
+ return;
+ }
+
+ if(numberofleds >= 10)
+ {
+ packet[1] = DEVICE_ERROR_LED_COUNT;
+ raw_hid_send(packet,32);
+ return;
+ }
+
+ for (uint8_t i = 0; i < numberofleds; i++)
+ {
+ uint8_t offset = (i * 3) + 3;
+ uint8_t r = data[offset];
+ uint8_t g = data[offset + 1];
+ uint8_t b = data[offset + 2];
+
+ //if ( ((index + i) == CAPS_LOCK_LED_INDEX && host_keyboard_led_state().caps_lock) || ((index + i) == NUM_LOCK_LED_INDEX && host_keyboard_led_state().num_lock) || ((index + i) == SCROLL_LOCK_LED_INDEX && host_keyboard_led_state().scroll_lock)) {
+ //if ( ((index + i) == CAPS_LOCK_LED_INDEX && host_keyboard_led_state().caps_lock) || ((index + i) == NUM_LOCK_LED_INDEX && host_keyboard_led_state().num_lock)) {
+ //if ( (index + i) == CAPS_MAC_WIN_LED_INDEX && host_keyboard_led_state().caps_lock) {
+ //if ( (index + i) == CAPS_LOCK_LED_INDEX && host_keyboard_led_state().caps_lock) {
+ //if ( (index + i) == NUM_LOCK_LED_INDEX && host_keyboard_led_state().num_lock) {
+ //#if defined(RGBLIGHT_ENABLE)
+ //rgblight_setrgb_at(255, 255, 255, index + i);
+ //#elif defined(RGB_MATRIX_ENABLE)
+ //rgb_matrix_set_color(index + i, 255, 255, 255);
+ //#endif
+
+ //} else {
+
+ #if defined(RGBLIGHT_ENABLE)
+ rgblight_setrgb_at(r, g, b, index + i);
+ #elif defined(RGB_MATRIX_ENABLE)
+ rgb_matrix_set_color(index + i, r, g, b);
+ #endif
+ }
+ }
+//}
+
+void signalrgb_mode_enable(void)
+{
+ #if defined(RGB_MATRIX_ENABLE)
+ rgb_matrix_mode_noeeprom(RGB_MATRIX_SIGNALRGB); //Set RGB Matrix to SignalRGB Compatible Mode
+ #endif
+}
+
+void signalrgb_mode_disable(void)
+{
+ #if defined(RGBLIGHT_ENABLE)
+ rgblight_reload_from_eeprom();
+ #elif defined(RGB_MATRIX_ENABLE)
+ rgb_matrix_reload_from_eeprom(); //Reloading last effect from eeprom
+ #endif
+}
+
+void get_total_leds(void)//Grab total number of leds that a board has.
+{
+ packet[0] = GET_TOTAL_LEDS;
+ #if defined(RGBLIGHT_ENABLE)
+ packet[1] = RGBLIGHT_LED_COUNT;
+ #elif defined(RGB_MATRIX_ENABLE)
+ packet[1] = RGB_MATRIX_LED_COUNT;
+ #endif
+
+ raw_hid_send(packet, 32);
+}
+
+void get_firmware_type(void) //Grab which fork of qmk a board is running.
+{
+ packet[0] = GET_FIRMWARE_TYPE;
+ packet[1] = FIRMWARE_TYPE_BYTE;
+
+ raw_hid_send(packet, 32);
+}
+
+bool srgb_raw_hid_rx(uint8_t *data, uint8_t length) {
+ switch (data[0]) {
+ case GET_QMK_VERSION:
+
+ get_qmk_version();
+
+ break;
+ case GET_PROTOCOL_VERSION:
+
+ get_signalrgb_protocol_version();
+
+ break;
+ case GET_UNIQUE_IDENTIFIER:
+
+ get_unique_identifier();
+
+ break;
+ case STREAM_RGB_DATA:
+
+ led_streaming(data);
+
+ break;
+
+ case SET_SIGNALRGB_MODE_ENABLE:
+
+ signalrgb_mode_enable();
+
+ break;
+
+ case SET_SIGNALRGB_MODE_DISABLE:
+
+ signalrgb_mode_disable();
+
+ break;
+
+ case GET_TOTAL_LEDS:
+
+ get_total_leds();
+
+ break;
+
+ case GET_FIRMWARE_TYPE:
+
+ get_firmware_type();
+
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+#if defined(VIA_ENABLE)
+__attribute__((weak)) bool via_command_kb(uint8_t *data, uint8_t length) {
+ return srgb_raw_hid_rx(data, length);
+}
+#else
+void raw_hid_receive(uint8_t *data, uint8_t length) {
+ srgb_raw_hid_rx(data, length);
+}
+#endif
diff --git a/quantum/signalrgb.h b/quantum/signalrgb.h
new file mode 100644
index 000000000000..de909672ec5a
--- /dev/null
+++ b/quantum/signalrgb.h
@@ -0,0 +1,82 @@
+#pragma once
+
+enum signalrgb_commands
+{
+ GET_QMK_VERSION = 0x21,
+ GET_PROTOCOL_VERSION = 0x22,
+ GET_UNIQUE_IDENTIFIER = 0x23,
+ STREAM_RGB_DATA = 0x24,
+ SET_SIGNALRGB_MODE_ENABLE = 0x25,
+ SET_SIGNALRGB_MODE_DISABLE = 0x26,
+ GET_TOTAL_LEDS = 0x27,
+ GET_FIRMWARE_TYPE = 0x28,
+};
+
+enum signalrgb_responses
+{
+ PROTOCOL_VERSION_BYTE_1 = 1,
+ PROTOCOL_VERSION_BYTE_2 = 0,
+ PROTOCOL_VERSION_BYTE_3 = 6,
+ DEVICE_UNIQUE_IDENTIFIER_BYTE_1 = 0,
+ DEVICE_UNIQUE_IDENTIFIER_BYTE_2 = 0,
+ DEVICE_UNIQUE_IDENTIFIER_BYTE_3 = 0,
+ FIRMWARE_TYPE_BYTE = 2,
+ DEVICE_ERROR_LED_BOUNDS = 253,
+ DEVICE_ERROR_LED_COUNT = 254
+};
+
+void get_qmk_version(void);
+void get_signalrgb_protocol_version(void);
+void get_unique_identifier(void);
+void led_streaming(uint8_t *data);
+void signalrgb_mode_enable(void);
+void signalrgb_mode_disable(void);
+void get_total_leds(void);
+
+//Changelogs for Firmware Versions------------------------------------
+//V1.0.1 added detection for the total number of LEDs a board has. Plugins need a rewrite to make use of this change. Rewritten plugins will not function with older firmware.
+//V1.0.2 added another detection byte for which fork of SignalRGB firmware device is running. This means we can keep overlap Unique Identifiers between Sonix and Mainline.
+//V1.0.3 unifies the command IDs between Mainline, Sonix, and VIA. All commands have a 0x20 offset now.
+//V1.0.4 improves detection handling, and has a complete rewrite of the plugins. Also merges Mainline and VIA branches. VIA Branch is deprecated.
+//V1.0.5 adds support for RGBLight and adds proper bounds checking for leds to ensure we don't crash the firmware.
+//V1.0.6 fixes conflict with VIA running at the same time.
+
+/* keymap default:
+DEVICE, DEVICE_UNIQUE_IDENTIFIER_BYTE_1,DEVICE_UNIQUE_IDENTIFIER_BYTE_2,DEVICE_UNIQUE_IDENTIFIER_BYTE_3
+gmmk/pro/rev1/ansi:default 0 0 5
+gmmk/pro/rev2/ansi:default 0 0 5
+gmmk/pro/rev1/iso:default 0 0 6
+gmmk/pro/rev2/iso:default 0 0 6
+gmmk/gmmk2/p65/ansi:default 0 0 7
+gmmk/gmmk2/p65/iso:default 0 0 8
+gmmk/gmmk2/p96/ansi:default 0 0 9
+gmmk/gmmk2/p96/iso:default 0 0 10
+redragon/k530:default 0 1 1
+redragon/k552/rev1:ansi 0 1 2
+redragon/k552/rev1:iso 0 1 2
+redragon/k552/rev2:default 0 1 2
+redragon/k552/rev2:abnt2 0 1 2
+redragon/k556:default 0 1 3
+redragon/k580:default 0 1 4
+redragon/k582:default 0 1 3
+redragon/k630:default 0 1 5
+*/
+
+
+/* keymap via:
+DEVICE, DEVICE_UNIQUE_IDENTIFIER_BYTE_1,DEVICE_UNIQUE_IDENTIFIER_BYTE_2,DEVICE_UNIQUE_IDENTIFIER_BYTE_3
+gmmk/pro/rev1/ansi:via 0 0 5
+gmmk/pro/rev2/ansi:via 0 0 5
+gmmk/pro/rev1/iso:via 0 0 6
+gmmk/pro/rev2/iso:via 0 0 6
+gmmk/gmmk2/p65/ansi:via 0 0 7
+gmmk/gmmk2/p65/iso:via 0 0 8
+gmmk/gmmk2/p96/ansi:via 0 0 9
+gmmk/gmmk2/p96/iso:via 0 0 10
+gmmk/gmmk3/p65/ansi:via 0 0 11
+gmmk/gmmk3/p65/iso:via 0 0 12
+gmmk/gmmk3/p75/ansi:via 0 0 13
+gmmk/gmmk3/p75/iso:via 0 0 14
+gmmk/gmmk3/p100/ansi:via 0 0 15
+gmmk/gmmk3/p100/iso:via 0 0 16
+*/
diff --git a/readme.md b/readme.md
index f0e49a08e956..902f00310af1 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,20 @@
+# QMK Firmware with SignalRGB Support for Nuphy
+
+## Improvements:
+
+1. Added SignalRGB support for Nuphy Keyboards using qmk firmware. To enable SignalRGB, you should set "SIGNALRGB_ENABLE = yes" in rules.mk.
+2. Fixed some keymapping errors in nuphy keyboard plugin for Air96 V2 from [SignalRGB](https://github.com/SRGBmods/qmk-plugins/blob/main/Nuphy/NuPhy_Air96v2_QMK_ANSI_Keyboard.js). Plugins are saved in [SRGB_qmk_plugins](/SRGB_qmk_plugins). You should copy .js file to C:\Users\\[YOUR USERNAME]\Documents\WhirlwindFX\Plugins.
+3. Added SignalRGB plugin for Air60 v2 (untested).
+5. Improved keymap accuracy of SignalRGB plugin for Air75 v2.
+6. Added reverse NumLock indicator for Air96 V2.
+7. Changed battery indicator for Air96 v2: Leds appears yellow when 50-95%, rather than orange.
+
+## Note: SignalRGB / VIA Issue
+(From [document](https://docs.signalrgb.com/qmk) provided by SignalRGB)
+
+Please note that SignalRGB and VIA do not happily run at the same time. You must quit either VIA (or close the browser tab) before running SignalRGB, or close SignalRGB before attempting to use VIA as the raw_hid_recieve solution that both Software(s) use conflict with each other.
+
+---
# Quantum Mechanical Keyboard Firmware
[](https://github.com/qmk/qmk_firmware/tags)