Add support for Microsoft Natural Keyboard 4000

This is a great keyboard, but made for PCs. I'm not sure if it's possible given that, but it would be great if the top row of keys (home, search, mail, favorites 1-5, calculator) as well as the middle scroll button and lower back/forward keys could be recognized by BTT.

https://www.microsoft.com/accessories/en-us/products/keyboards/natural-ergonomic-keyboard-4000/b2m-00012

https://images-na.ssl-images-amazon.com/images/I/818CpANxtEL.SL1500.jpg

BTW, I love BTT so much!!!

I'm looking into more generic USB support, however I currently don't have a timeline.
You could already use ControllerMate for this: http://www.orderedbytes.com/controllermate/compatibility/#microsoft/ergonomic_keyboard_4000

Generic USB support seems to recognise this Keyboard but it's quite cumbersome to use this to trigger actions. Would it make sense to add a specific trigger set for those kind of "consumer input devices"?

Vendor ID is 0x45e and Product ID 0xdb. The shown "Pair" is 0x6 Page 0x1. When I use the analyser I see the following keys:

Zoom In: 01 2d 02 00 00 01 00 00
Zoom Out: 01 2e 02 00 00 01 00 00

I can provide the ones for Home, E-Mail, 1-5, Favorites and Back/Forward as well :slight_smile:

somebody just needs to create the analyzer once, then it can be shared among users. Based on your details the script would look like this

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(reportBuffer.readUInt8(1) === 0x2d) {
        log('trigger zoom in!');
        bttTriggerDeviceTrigger(targetDevice, 'zoomin');
    } else  if(reportBuffer.readUInt8(1) === 0x2e) {
        log('trigger zoom in!');
        bttTriggerDeviceTrigger(targetDevice, 'zoomout');
    }


    // If you want to get the next report even though,
    // the data has not changed, call this function:
    // bttGetNextEvenWithoutChange(targetDevice, reportID)
}
1 Like

Thanks for the very fast reply. I have to admit, that I don't know yet, how to use it. I added the script to the generic device configuration and entered both Trigger names in the "Provided Triggers" section. My reasoning was, that I also add an additional configuration within Generic devices where I'm already able to select one of the "provided triggers" and most interestingly HID display works but any assigned actions are not triggered.

in my script the trigger names are all lowercase, unless you changed them in the script, they need to be exactly the same in "provided triggers"

I saw the difference and already set the right names. However there seems to be something specific with the zoom in/out "keys" (rather: jog/shuttles) - probably they trigger several times. Currently it works only once, then I have to go in the Generic Device configuration, click somewhere until the "Save" button appears again - when I click it, it works again once.

I'll try again with the other keys...

AH in this case uncomment the last line:

bttGetNextEvenWithoutChange(targetDevice, reportID)
1 Like

Works like a charm - great!! Here's the full script which provides triggers for

ZoomIn
ZoomOut
Home
E-Mail
Back
Forward
Calculator
Favorites
1
2
3
4
5
// 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(reportBuffer.readUInt8(1) === 0x2d) {
        bttTriggerDeviceTrigger(targetDevice, 'ZoomIn');
    }
    else if(reportBuffer.readUInt8(1) === 0x2e) {
        bttTriggerDeviceTrigger(targetDevice, 'ZoomOut');
    }
    else if(reportBuffer.readUInt8(1) === 0x23) {
        bttTriggerDeviceTrigger(targetDevice, 'Home');
    }
    else if(reportBuffer.readUInt8(1) === 0x8a) {
        bttTriggerDeviceTrigger(targetDevice, 'E-Mail');
    }
    else if(reportBuffer.readUInt8(1) === 0x24) {
        bttTriggerDeviceTrigger(targetDevice, 'Back');
    }
    else if(reportBuffer.readUInt8(1) === 0x25) {
        bttTriggerDeviceTrigger(targetDevice, 'Forward');
    }
    else if(reportBuffer.readUInt8(1) === 0x92) {
        bttTriggerDeviceTrigger(targetDevice, 'Calculator');
    }
    else if(reportBuffer.readUInt8(1) === 0x82) {
        bttTriggerDeviceTrigger(targetDevice, 'Favorites');
    }
    else if(reportBuffer.readUInt8(5) === 0x05) {
        bttTriggerDeviceTrigger(targetDevice, '1');
    }
    else if(reportBuffer.readUInt8(5) === 0x09) {
        bttTriggerDeviceTrigger(targetDevice, '2');
    }
    else if(reportBuffer.readUInt8(5) === 0x11) {
        bttTriggerDeviceTrigger(targetDevice, '3');
    }
    else if(reportBuffer.readUInt8(5) === 0x21) {
        bttTriggerDeviceTrigger(targetDevice, '4');
    }
    else if(reportBuffer.readUInt8(5) === 0x41) {
        bttTriggerDeviceTrigger(targetDevice, '5');
    }

    // If you want to get the next report even though,
    // the data has not changed, call this function:
    bttGetNextEvenWithoutChange(targetDevice, reportID)
}

// 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
}
1 Like

Nice! Would be great if you could right-click the analyzer and export it to a file, so others can just import it