Generic Devices: Logitech m720 mouse

Hi

im running latest alpha of BTT on monterey

i figured out to log all buttons but they dont show up in provided triggers. am i missing something

i closed and open analyse window few times to repeat process

i will only use thumb button for mission control. it working that way if i connect with dongle but its works as page up on bluetooth connection. just weird choice from logitech

i tried to put callBTT('trigger_action',{json:{BTTPredefinedActionType:5}}) code on thumb statement to try my luck it didnt worked, it would be cool

here is code

// 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).
b=(index,bytes)=>(reportBuffer.readUInt8(index)===bytes)

if(b(1,0x01) && b(0,0x02)){
    log('Left');
    bttTriggerDeviceTrigger(targetDevice,'Left');
}else if(b(1,0x02)){
    log('Right');
    bttTriggerDeviceTrigger(targetDevice,'Right');
}else if(b(1,0x04)){
    log('Middle');
    bttTriggerDeviceTrigger(targetDevice,'Middle');
}else if(b(1,0x10)){
    log('Forward');
    bttTriggerDeviceTrigger(targetDevice,'Forward');
}else if(b(1,0x08)){
    log('Backward');
    bttTriggerDeviceTrigger(targetDevice,'Backward');
}else if(b(6,0x01)){
    log('Scroll Up');
    bttTriggerDeviceTrigger(targetDevice,'ScrollUp');
}else if(b(6,0xff)){
    log('Scroll Down');
    bttTriggerDeviceTrigger(targetDevice,'ScrollDown');
}else if(b(7,0x01)){
    log('Scroll Right');
    bttTriggerDeviceTrigger(targetDevice,'ScrollRight');
}else if(b(7,0xff)){
    log('Scroll Left');
    bttTriggerDeviceTrigger(targetDevice,'ScrollLeft');
}else if(b(2,0x52)){
    log('Thumb');
    bttTriggerDeviceTrigger(targetDevice,'Thumb');
}
}

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

i thought thumb button will work as configured while configuration window is open
after closing window it started to working, using callBTT or i guess all other BTT functions work on there too
so adding callBTT('trigger_action',{json:{BTTPredefinedActionType:5}}) does trick for me

but i couldn't see my buttons on Provided Triggers like on documentation

Great work, thanks for adding generic devices

you need to enter the button names yourself, BTT knows nothing about the device :slight_smile:

lol never look Provided Triggers as a input box
i thought BTT automatically fills with bttTriggerDeviceTrigger(targetDevice,'Forward');

i tried like this but i couldnt get mod key, if its not possible i will try regular method :slight_smile:

analyzeDeviceInput=async(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).
    b=(i,b)=>(reportBuffer.readUInt8(i)===b)
    
    p=i=>(callBTT('trigger_action',{json:{BTTPredefinedActionType:i}}))
    MOD=(await callBTT('get_string_variable',{variableName:'currently_pressed_keyboard_keys'})).split(",")[0]*1
    if(b(2,0x52)){
        log('Thumb');
        bttTriggerDeviceTrigger(targetDevice,'Thumb');
        switch(MOD){
        // CMD - APP EXPOSE
        case 55:p(6);break
        // OPTION - PLAY PAUSE
        case 58:p(23);break
        // CONTROL - MUTE / UNMUTE
        case 59:p(22);break
        // FN - 
        // case 63:;break
        // WITHOUT MODIFIER - MISSION CONTROL
        default:p(5);break
        }
    }else if(b(1,0x10)){
        log('Forward');
        bttTriggerDeviceTrigger(targetDevice,'Forward');
        switch(MOD){
        // OPTION - NEXT
        case 58:p(26);break
        // CONTROL - VOLUME UP
        case 59:p(24);break
        }
    }else if(b(1,0x08)){
        log('Backward');
        bttTriggerDeviceTrigger(targetDevice,'Backward');
        switch(MOD){
        // OPTION - PREVIOUS
        case 58:p(27);break
        // CONTROL - VOLUME DOWN
        case 59:p(25);break
        }
    }
 }

You could always call Apple Script from within the Java Script via

let result = await runAppleScript({script: appleScript});

However you can also use this to get the currently pressed keys via BTT, which should be more efficient:

let pressedKeys = await callBTT('get_string_variable', {variable_name:'currently_pressed_keyboard_keys'});

if(pressedKeys.indexOf("55,") > -1) {
// left cmd
}
if(pressedKeys.indexOf("58,") > -1) {
// left opt
}
if(pressedKeys.indexOf("56,") > -1) {
// left shift
}

if(pressedKeys.indexOf("54,") > -1) {
// right cmd
}

if(pressedKeys.indexOf("61,") > -1) {
// right opt
}

if(pressedKeys.indexOf("60,") > -1) {
// right shift
}


Weirdly not workin

it sees keys, then i mapped in Provided Triggers

selected from menu

added mappings as javascript, its working if i click to run script button. also tried with modifier keys. they working too if click run script button with modifier keys

(async()=>{

let m=await callBTT('get_string_variable',{variableName:'currently_pressed_keyboard_keys'})
MOD=m.length>1?m:[]
k=b=>(MOD.includes(b)) // i guess this way multiple modifiers should work too

p=i=>(callBTT('trigger_action',{json:{BTTPredefinedActionType:i}}))

// CMD - APP EXPOSE
     if(k("55")){p(6)}
// OPTION - PLAY PAUSE
else if(k("58")){p(23)}
// CONTROL - MUTE / UNMUTE
else if(k("59")){p(22)}
// WITHOUT MODIFIER - MISSION CONTROL
else            {p(5)}

returnToBTT(MOD)
})()

But it didn't trigger Thumb action

so i tried to create a named trigger as alternative way, still same result

analyzeDeviceInput=(targetDevice,reportID,reportDataHex)=>{   
    let reportBuffer=buffer.Buffer.from(reportDataHex,'hex');
    b=(i,b)=>(reportBuffer.readUInt8(i)===b)

    if(b(2,0x52)){
        log('Thumb')
        callBTT('trigger_named',{trigger_name:'m720Thumb'})
    }
}

I haven't looked at all the code yet, but currently_pressed_keyboard_keys is not an array, it's a string of comma separated key numbers

ok np, no need to hurry, i split with comma to get an array. checked length of it, otherwise it throws error on split when strings is empty. this way i get array of all pressed modifier keys. i can use like this

if(k("55") && (k("58")){predefined(6)}

@Andreas_Hegenberg

what is the difference between two

// 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).
b=(index,bytes)=>(reportBuffer.readUInt8(index)===bytes)

    if(b(2,0x52)){
        log('Thumb')
        callBTT('trigger_named',{trigger_name:'m720Thumb'})
    }
}
analyzeDeviceInput=(targetDevice,reportID,reportDataHex)=>{   
    let reportBuffer=buffer.Buffer.from(reportDataHex,'hex');
    b=(i,b)=>(reportBuffer.readUInt8(i)===b)

    if(b(2,0x52)){
        log('Thumb')
        callBTT('trigger_named',{trigger_name:'m720Thumb'})
    }
}

top one is working

I should probably make this more clear, it's not only the signature but the whole "function header". BTT needs the exact function definition, otherwise it will not be able to use it.

But here you aren't splitting anything, or do I miss something?

you r right :slight_smile: yep checked code, i wrote it in my mind but didn't added to BTT

(async()=>{

let m=await callBTT('get_string_variable',{variableName:'currently_pressed_keyboard_keys'})
MOD=m.length>1?m.split(","):[]
k=b=>(MOD.includes(b))

p=i=>(callBTT('trigger_action',{json:{BTTPredefinedActionType:i}}))

// CMD - APP EXPOSE
     if(k("55")){p(6)}
// OPTION - PLAY PAUSE
else if(k("58")){p(23)}
// CONTROL - MUTE / UNMUTE
else if(k("59")){p(22)}
// WITHOUT MODIFIER - MISSION CONTROL
else            {p(5)}

returnToBTT(k("55"))
})()

this script running ok when i use with run script button but i guess it triggers all conditions if i trigger with mouse button. i tried to add a small timeout but not worked

I'm currently not 100% sure as I'm on my phone, but I think the json parameter of trigger_action needs to be a string.
Maybe
p=i=>(callBTT('trigger_action',{json:JSON.stringify({BTTPredefinedActionType:i})}))

will help

this one is really weird, how can be possible to all if statements can run on same time,
its pushing all buttons :smiley: it tries to page up, app expose, mute audio, play pause & mission control.. i will try with applescript later, also is there a way to block default event for button

Yes that's really weird and should not be possible with if /else. I suspect the issue is coming from somewhere else.

Blocking default events would only be possible if you can get exclusive access to the device:

However don't spend too much time on Logitech mice. They will be supported natively in BTT once I have finished porting GitHub - libratbag/libratbag: A DBus daemon to configure input devices, mainly high-end and gaming mice to BTT.

You could add some logs via BTTLog(), these would show up in the log files in ~/Library/Application Support/BetterTouchTool/Logs

thanks for your support, i will wait for port, have a nice day

com.hegenberg.BetterTouchTool 2023-02-05--21-45-37-274.log.zip (47.9 KB)

Until that is finished, the easiest way to customize the logitech mice is by assigning keyboard shortcuts to the buttons in the logitech software, then matching these shortcuts in the keyboard section in BTT :slight_smile:

Thank for suggesting but i was stopped to use their software 10 years ago. changed 5 logitech mouses over time. products mostly nice but software is not working great.

i tried with applescript

set AppleScript's text item delimiters to ","
tell application "BetterTouchTool"
	set m to text items of (get_string_variable "currently_pressed_keyboard_keys")
	
	--for multiple modifier keys
	--if m contains "55" and m contains "58" then
	if m contains "55" then
		-- CMD - APP EXPOSE
		trigger_action "{\"BTTPredefinedActionType\":6}"
	else if m contains "58" then
		-- OPTION - PLAY PAUSE
		trigger_action "{\"BTTPredefinedActionType\":23}"
	else if m contains "59" then
		-- CONTROL - MUTE / UNMUTE
		trigger_action "{\"BTTPredefinedActionType\":22}"
	else
		--WITHOUT MODIFIER - MISSION CONTROL
		trigger_action "{\"BTTPredefinedActionType\":5}"
	end if
end tell

but result is same it triggering all statements and triggering Page Up

tell application "BetterTouchTool"
trigger_action "{\"BTTPredefinedActionType\":5}"
end tell

this one only opens mission control and triggering Page Up
page up is default mapping for key

couldnt open app expose with this way too

also tried with advanced conditions
Screen Shot 2023-02-07 at 04.17.20

Your script seems to work fine in a different context I tested it with, I can't think of any reason why it would trigger multiple times for you.

However in 4.020 alpha (uploading now) I have also fixed the default modifier options. So you can now do this in the BTT UI:

it handles better, at least %60 of time its working as expected. maybe my problem it sending page up at same time. i will report back my experience again after libratag porting completes

i set up back-forward buttons in normal mouse. its all working normally

Screen Shot 2023-02-07 at 14.46.24