Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0xdevalias/3cfb3512e708b58098a315043cbaefaa to your computer and use it in GitHub Desktop.
Save 0xdevalias/3cfb3512e708b58098a315043cbaefaa to your computer and use it in GitHub Desktop.
BetterTouchTool Generic Device Trigger PoC for Razer Ouroboros gaming mouse

BetterTouchTool Generic Device Trigger PoC for Razer Ouroboros gaming mouse

https://updates.folivora.ai/bettertouchtool_release_notes.html

  • BetterTouchTool >= 4.0 (January, 30, 2023)

    • New Trigger Section: "Generic Devices". With this it becomes possible to make most input devices (e.g. remotes, special mice, buttons etc.) work with BTT, using some Java Script.

https://docs.folivora.ai/docs/1500_generic_devices.html

Tweets

Using this feature for the Razer Ouroboros: https://twitter.com/_devalias/status/1621421090101948416

Using this feature for the Native Instruments Traktor F1: https://twitter.com/_devalias/status/1621428879721373696

Parse Device Input/Output script

let isButtonDown = false

// Enter your input analyzer script here. 
// Do not change the function signatures
function analyzeDeviceInput(targetDevice, reportID, reportDataHex) {
	let reportBuffer = buffer.Buffer.from(reportDataHex, 'hex');
        // the values you see above are in hex format. To read such a byte
        // use readUInt8(index).
        
        if (reportID === 0) {
            let scrollByte = reportBuffer.readUInt8(3)
            let isScrolling = scrollByte === 0x01 || scrollByte === 0xff

            if (reportBuffer.readUInt8(0) === 0x01) {
                isButtonDown = true
                log('leftButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'leftButtonDown')
            }
            else if (reportBuffer.readUInt8(0) === 0x02) {
                isButtonDown = true
                log('rightButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'rightButtonDown')
            }
            else if (reportBuffer.readUInt8(0) === 0x04) {
                isButtonDown = true
                log('middleButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'middleButtonDown')
            }
            else if (reportBuffer.readUInt8(0) === 0x10) {
                isButtonDown = true
                log('forwardThumbButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'forwardThumbButtonDown')
            }
            else if (reportBuffer.readUInt8(0) === 0x08) {
                isButtonDown = true
                log('backThumbButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'backThumbButtonDown')
            }
            else if (reportBuffer.readUInt8(0) === 0x00 && isButtonDown && !isScrolling) {
                isButtonDown = false
                log('buttonUp')

                bttTriggerDeviceTrigger(targetDevice, 'buttonUp')
            }

            // if (reportBuffer.readUInt8(3) === 0x01) {
            //     log('scrollForward')
            // }
            // else if (reportBuffer.readUInt8(3) === 0xff) {
            //     log('scrollBackward')
            // }
        }
        else if (reportID === 4) {
            if (reportBuffer.readUInt8(1) === 0x20) {
                log('middleSensitivityTopButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'middleSensitivityTopButtonDown')
            }
            else if (reportBuffer.readUInt8(1) === 0x21) {
                log('middleSensitivityBottomButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'middleSensitivityBottomButtonDown')
            }
            else if (reportBuffer.readUInt8(1) === 0x25) {
                log('leftClutchButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'leftClutchButtonDown')
            }
            else if (reportBuffer.readUInt8(1) === 0x24) {
                log('rightClutchButtonDown')

                bttTriggerDeviceTrigger(targetDevice, 'rightClutchButtonDown')
            }
            else if (reportBuffer.readUInt8(1) === 0x00) {
                log('reportID4ButtonUp')

                bttTriggerDeviceTrigger(targetDevice, 'reportID4ButtonUp')
            }
        }
}

// Advanced, optional. Use if you want to trigger commands that send data to
// the device, from a BTT predefined action.
// See https://docs.folivora.ai/1500_generic_devices.html
// async function executeBTTCommand(targetDevice, commandName, commandInput) {
//     log("execute command: " + commandName)
//     switch(commandName) {
//         case "exampleCommand": {
//             // send any hex string to the device
//             let deviceResponse = await bttSendDataToDevice({
//               BTTActionSendDataTargetDevice: targetDevice,
//               BTTActionSendDataData: 'FEEDC0DE',
//               BTTActionSendDataReportType: 1,
//               BTTActionSendDataResponseType: -1,
//               BTTActionSendDataResponsePrefix: ''
//             }); 
//             break;
//         }
//     }
//     return 'done executing ' + commandName
// }

Trigger Definition

[
  {
    "BTTGestureNotes" : "Razer Ouroboros",
    "BTTTriggerType" : 763,
    "BTTTriggerTypeDescription" : "Generic Device & Input Analyzer",
    "BTTTriggerClass" : "BTTTriggerTypeGenericDevice",
    "BTTPredefinedActionType" : -1,
    "BTTPredefinedActionName" : "No Action",
    "BTTEnabled2" : 1,
    "BTTAlternateModifierKeys" : 0,
    "BTTRepeatDelay" : 0,
    "BTTUUID" : "3DE7DF79-6C7B-4844-A3D6-66907EAEC55A",
    "BTTNotesInsteadOfDescription" : 0,
    "BTTEnabled" : 1,
    "BTTModifierMode" : 0,
    "BTTOrder" : 0,
    "BTTDisplayOrder" : 0,
    "BTTGenericDeviceConfiguration" : {
      "BTTGenericDeviceUsagePair4" : {
        "BTTGenericDeviceUsagePage" : "",
        "BTTGenericDeviceUsage" : ""
      },
      "BTTGenericDeviceVendorID" : "0x1532",
      "BTTGenericDeviceWatchedBytes" : {
        "0" : [
          0,
          3
        ],
        "4" : [
          1
        ]
      },
      "BTTGenericDeviceUsagePair3" : {
        "BTTGenericDeviceUsagePage" : "",
        "BTTGenericDeviceUsage" : ""
      },
      "BTTGenericDeviceActiveReports" : {
        "0" : true,
        "4" : true
      },
      "BTTGenericDeviceProvidedTriggers" : "leftButtonDown\nrightButtonDown\nmiddleButtonDown\nforwardThumbButtonDown\nbackThumbButtonDown\nbuttonUp\nmiddleSensitivityTopButtonDown\nmiddleSensitivityBottomButtonDown\nleftClutchButtonDown\nrightClutchButtonDown",
      "BTTGenericDeviceUsagePair2" : {
        "BTTGenericDeviceUsagePage" : "",
        "BTTGenericDeviceUsage" : ""
      },
      "BTTGenericDeviceProductID" : "0x32",
      "BTTGenericDeviceName" : "Razer Ouroboros",
      "BTTGenericDeviceScript" : "let isButtonDown = false\n\n\/\/ Enter your input analyzer script here. \n\/\/ Do not change the function signatures\nfunction analyzeDeviceInput(targetDevice, reportID, reportDataHex) {\n\tlet reportBuffer = buffer.Buffer.from(reportDataHex, 'hex');\n        \/\/ the values you see above are in hex format. To read such a byte\n        \/\/ use readUInt8(index).\n        \n        if (reportID === 0) {\n            let scrollByte = reportBuffer.readUInt8(3)\n            let isScrolling = scrollByte === 0x01 || scrollByte === 0xff\n\n            if (reportBuffer.readUInt8(0) === 0x01) {\n                isButtonDown = true\n                log('leftButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'leftButtonDown')\n            }\n            else if (reportBuffer.readUInt8(0) === 0x02) {\n                isButtonDown = true\n                log('rightButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'rightButtonDown')\n            }\n            else if (reportBuffer.readUInt8(0) === 0x04) {\n                isButtonDown = true\n                log('middleButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'middleButtonDown')\n            }\n            else if (reportBuffer.readUInt8(0) === 0x10) {\n                isButtonDown = true\n                log('forwardThumbButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'forwardThumbButtonDown')\n            }\n            else if (reportBuffer.readUInt8(0) === 0x08) {\n                isButtonDown = true\n                log('backThumbButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'backThumbButtonDown')\n            }\n            else if (reportBuffer.readUInt8(0) === 0x00 && isButtonDown && !isScrolling) {\n                isButtonDown = false\n                log('buttonUp')\n\n                bttTriggerDeviceTrigger(targetDevice, 'buttonUp')\n            }\n\n            \/\/ if (reportBuffer.readUInt8(3) === 0x01) {\n            \/\/     log('scrollForward')\n            \/\/ }\n            \/\/ else if (reportBuffer.readUInt8(3) === 0xff) {\n            \/\/     log('scrollBackward')\n            \/\/ }\n        }\n        else if (reportID === 4) {\n            if (reportBuffer.readUInt8(1) === 0x20) {\n                log('middleSensitivityTopButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'middleSensitivityTopButtonDown')\n            }\n            else if (reportBuffer.readUInt8(1) === 0x21) {\n                log('middleSensitivityBottomButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'middleSensitivityBottomButtonDown')\n            }\n            else if (reportBuffer.readUInt8(1) === 0x25) {\n                log('leftClutchButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'leftClutchButtonDown')\n            }\n            else if (reportBuffer.readUInt8(1) === 0x24) {\n                log('rightClutchButtonDown')\n\n                bttTriggerDeviceTrigger(targetDevice, 'rightClutchButtonDown')\n            }\n            else if (reportBuffer.readUInt8(1) === 0x00) {\n                log('reportID4ButtonUp')\n\n                bttTriggerDeviceTrigger(targetDevice, 'reportID4ButtonUp')\n            }\n        }\n}\n\n\/\/ Advanced, optional. Use if you want to trigger commands that send data to\n\/\/ the device, from a BTT predefined action.\n\/\/ See https:\/\/docs.folivora.ai\/1500_generic_devices.html\n\/\/ async function executeBTTCommand(targetDevice, commandName, commandInput) {\n\/\/     log(\"execute command: \" + commandName)\n\/\/     switch(commandName) {\n\/\/         case \"exampleCommand\": {\n\/\/             \/\/ send any hex string to the device\n\/\/             let deviceResponse = await bttSendDataToDevice({\n\/\/               BTTActionSendDataTargetDevice: targetDevice,\n\/\/               BTTActionSendDataData: 'FEEDC0DE',\n\/\/               BTTActionSendDataReportType: 1,\n\/\/               BTTActionSendDataResponseType: -1,\n\/\/               BTTActionSendDataResponsePrefix: ''\n\/\/             }); \n\/\/             break;\n\/\/         }\n\/\/     }\n\/\/     return 'done executing ' + commandName\n\/\/ }",
      "BTTGenericDeviceSelectedTab" : 1,
      "BTTGenericDeviceDropdownSelection" : {
        "manufacturer" : "Razer",
        "product" : "Razer Ouroboros",
        "productID" : 50,
        "usagePairs" : [
          {
            "DeviceUsagePage" : 1,
            "DeviceUsage" : 6
          },
          {
            "DeviceUsagePage" : 12,
            "DeviceUsage" : 1
          },
          {
            "DeviceUsagePage" : 1,
            "DeviceUsage" : 128
          },
          {
            "DeviceUsagePage" : 1,
            "DeviceUsage" : 0
          }
        ],
        "vendorID" : 5426
      },
      "BTTGenericDeviceUsagePair1" : {
        "BTTGenericDeviceUsagePage" : "",
        "BTTGenericDeviceUsage" : ""
      }
    }
  }
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment