Repository: pwr-Solaar/Solaar

Issue:
Add support of mouse gestures
**Information**
<!-- Please update to Solaar from this repository before asking for a new feature. -->
- Solaar version (`solaar --version` and `git describe --tags`): 1.0.5
- Distribution: KUbuntu 20.10
- Kernel version (ex. `uname -srmo`): Linux 5.8.0-44-generic x86_64 GNU/Linux
- Output of `solaar show` for the target device (if applicable):

<details>

```
Unifying Receiver
  Device path  : /dev/hidraw0
  USB id       : 046d:C52B
  Serial       : C3C1108B
    Firmware   : 24.11.B0036
    Bootloader : 02.09
    Other      : AA.AC
  Has 2 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 1=79, 2=247

  1: MX Keys Keyboard
     Device path  : /dev/hidraw1
     WPID         : 408A
     Codename     : MX Keys
     Kind         : keyboard
     Protocol     : HID++ 4.5
     Polling rate : 20 ms (50Hz)
     Serial number: D853A210
     Model ID:      B35B408A0000
     Unit ID:       D853A210
        Bootloader: BL1 08.00.B0011
          Firmware: MPK 12.00.B0011
             Other: 
     The power switch is located on the edge of top right corner.
     Supports 33 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BL1 08.00.B0011 00008169E8BB
            Firmware: Firmware MPK 12.00.B0011 408A8169E8BB
            Firmware: Other   
            Unit ID: D853A210  Model ID: B35B408A0000  Transport IDs: {'btleid': 'B35B', 'wpid': '408A'}
         3: DEVICE NAME            {0005}   
            Name: MX Keys Wireless Keyboard
            Kind: keyboard
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: DEVICE FRIENDLY NAME   {0007}   
         7: BATTERY STATUS         {1000}   
            Battery: 100%, discharging, next level 50%.
         8: REPROG CONTROLS V4     {1B04}   
            Key/Button Diversion: {'209': 0, '210': 0, '211': 0, '199': 1, '200': 1, '224': 0, '225': 0, '110': 0, '226': 0, '227': 0, '228': 0, '229': 0, '230': 0, '231': 0, '232': 0, '233': 0, '10': 0, '191': 0, '234': 0, '111': 0, '236': 0, '235': 0}
         9: CHANGE HOST            {1814}   
            Change Host: 3:DESKTOP-BMCVF52
        10: HOSTS INFO             {1815}   
            Host 0 (paired): ANTIX-PC
            Host 1 (unpaired): 
            Host 2 (paired): DESKTOP-BMCVF52
        11: BACKLIGHT2             {1982}   
            Backlight: True
        12: K375S FN INVERSION     {40A3}   
            Сменить поведение Fx: False
        13: ENCRYPTION             {4100}   
        14: LOCK KEY STATE         {4220}   
        15: KEYBOARD DISABLE KEYS  {4521}   
            Disable keys: {'1': False, '2': False, '4': False, '8': False, '16': False}
        16: MULTIPLATFORM          {4531}   
            Set OS: Windows
        17: DFUCONTROL SIGNED      {00C2}   
        18: DEVICE RESET           {1802}   internal, hidden
        19: unknown:1803           {1803}   internal, hidden
        20: CONFIG DEVICE PROPS    {1806}   internal, hidden
        21: unknown:1813           {1813}   internal, hidden
        22: OOBSTATE               {1805}   internal, hidden
        23: unknown:1830           {1830}   internal, hidden
        24: unknown:1890           {1890}   internal, hidden
        25: unknown:1891           {1891}   internal, hidden
        26: unknown:18A1           {18A1}   internal, hidden
        27: unknown:1DF3           {1DF3}   internal, hidden
        28: unknown:1E00           {1E00}   hidden
        29: unknown:1EB0           {1EB0}   internal, hidden
        30: unknown:1861           {1861}   internal, hidden
        31: unknown:1A20           {1A20}   internal, hidden
        32: unknown:18B0           {18B0}   internal, hidden
     Has 24 reprogrammable keys:
         0: Host Switch Channel 1     , default: HostSwitch Channel 1        => HostSwitch Channel 1      
             nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
         1: Host Switch Channel 2     , default: HostSwitch Channel 2        => HostSwitch Channel 2      
             nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
         2: Host Switch Channel 3     , default: HostSwitch Channel 3        => HostSwitch Channel 3      
             nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
         3: Brightness Down           , default: Brightness Down             => Brightness Down           
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:1, group:0, group mask:empty
             reporting: diverted
         4: Brightness Up             , default: Brightness Up               => Brightness Up             
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:2, group:0, group mask:empty
             reporting: diverted
         5: Mission Control/Task View , default: Mission Control/Task View   => Mission Control/Task View 
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:3, group:0, group mask:empty
             reporting: default
         6: Dashboard Launchpad/Action Center, default: Dashboard Launchpad/Action Center => Dashboard Launchpad/Action Center
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:4, group:0, group mask:empty
             reporting: default
         7: Show Desktop              , default: Show Desktop                => Show Desktop              
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:5, group:0, group mask:empty
             reporting: default
         8: Backlight Down            , default: Backlight Down              => Backlight Down            
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:6, group:0, group mask:empty
             reporting: default
         9: Backlight Up              , default: Backlight Up                => Backlight Up              
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:7, group:0, group mask:empty
             reporting: default
        10: Previous Fn               , default: Previous                    => Previous                  
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:8, group:0, group mask:empty
             reporting: default
        11: Play/Pause Fn             , default: Play/Pause                  => Play/Pause                
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:9, group:0, group mask:empty
             reporting: default
        12: Next Fn                   , default: Next                        => Next                      
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:10, group:0, group mask:empty
             reporting: default
        13: Mute Fn                   , default: Mute                        => Mute                      
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:11, group:0, group mask:empty
             reporting: default
        14: Volume Down Fn            , default: Volume Down                 => Volume Down               
             is FN, FN sensitive, reprogrammable, divertable, persistently divertable, analytics key events, pos:12, group:0, group mask:empty
             reporting: default
        15: Volume Up Fn              , default: Volume Up                   => Volume Up                 
             nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        16: Calculator                , default: Calculator                  => Calculator                
             nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        17: Screen Capture/Print Screen, default: Screen Capture              => Screen Capture            
             nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        18: App Contextual Menu/Right Click, default: Right Click/App Contextual Menu => Right Click/App Contextual Menu
             nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        19: Lock PC                   , default: WindowsLock                 => WindowsLock               
             nonstandard, reprogrammable, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        20: Left Arrow                , default: Keyboard Left Arrow         => Keyboard Left Arrow       
             nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        21: Right Arrow               , default: Keyboard Right Arrow        => Keyboard Right Arrow      
             nonstandard, divertable, persistently divertable, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        22: F Lock                    , default: Do Nothing One              => Do Nothing One            
             is FN, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
        23: unknown:0034              , default: Do Nothing One              => Do Nothing One            
             nonstandard, analytics key events, pos:0, group:0, group mask:empty
             reporting: default
     Battery: 100%, discharging, next level 50%.

  2: MX Vertical Wireless Mouse
     Device path  : /dev/hidraw2
     WPID         : 407B
     Codename     : MX Vertical
     Kind         : mouse
     Protocol     : HID++ 4.5
     Polling rate : 8 ms (125Hz)
     Serial number: D8F09E6A
     Model ID:      B020407BC08A
     Unit ID:       B7847ED4
        Bootloader: BOT 78.00.B0009
          Firmware: MPM 16.00.B0009
             Other: 
     Supports 34 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BOT 78.00.B0009 AAE28E5B17FF
            Firmware: Firmware MPM 16.00.B0009 407B8E5B17FF
            Firmware: Other   
            Unit ID: B7847ED4  Model ID: B020407BC08A  Transport IDs: {'btleid': 'B020', 'wpid': '407B', 'usbid': 'C08A'}
         3: DEVICE NAME            {0005}   
            Name: MX Vertical Advanced Ergonomic Mouse
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: CRYPTO ID              {0021}   
         7: DEVICE FRIENDLY NAME   {0007}   
         8: BATTERY STATUS         {1000}   
            Battery: 90%, discharging, next level 50%.
         9: unknown:1002           {1002}   
        10: REPROG CONTROLS V4     {1B04}   
            Key/Button Actions: {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '253': 253}
            Key/Button Diversion: {'82': 0, '83': 0, '86': 0, '253': 1, '215': 0}
        11: PERSISTENT REMAPPABLE ACTION {1C00}   
        12: CHANGE HOST            {1814}   
            Change Host: 3:DESKTOP-BMCVF52
        13: HOSTS INFO             {1815}   
            Host 0 (unpaired): ANTIX-PC
            Host 1 (unpaired): 
            Host 2 (unpaired): DESKTOP-BMCVF52
        14: unknown:2250           {2250}   
        15: unknown:18B1           {18B1}   internal, hidden
        16: VERTICAL SCROLLING     {2100}   
            Roller type: standard
            Ratchet per turn: 24
            Scroll lines: 0
        17: LOWRES WHEEL           {2130}   
            Wheel Reports: HID
            Scroll Wheel HID++ Scrolling: False
        18: ADJUSTABLE DPI         {2201}   
            Чувствительность (DPI): 1000
        19: DEVICE RESET           {1802}   internal, hidden
        20: unknown:1803           {1803}   internal, hidden
        21: CONFIG DEVICE PROPS    {1806}   internal, hidden
        22: unknown:1813           {1813}   internal, hidden
        23: OOBSTATE               {1805}   internal, hidden
        24: unknown:1830           {1830}   internal, hidden
        25: unknown:1890           {1890}   internal, hidden
        26: unknown:1891           {1891}   internal, hidden
        27: unknown:18A1           {18A1}   internal, hidden
        28: unknown:1DF3           {1DF3}   internal, hidden
        29: unknown:1E00           {1E00}   hidden
        30: unknown:1EB0           {1EB0}   internal, hidden
        31: unknown:1E22           {1E22}   internal, hidden
        32: unknown:1861           {1861}   internal, hidden
        33: unknown:18C0           {18C0}   internal, hidden
     Has 7 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, reprogrammable, analytics key events, pos:0, group:1, group mask:g1
             reporting: default
         2: Middle Button             , default: Mouse Middle Button         => Mouse Middle Button       
             mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         3: Back Button               , default: Mouse Back Button           => Mouse Back Button         
             mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         4: Forward Button            , default: Mouse Forward Button        => Mouse Forward Button      
             mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         5: DPI Switch                , default: DPI Switch                  => DPI Switch                
             mse, reprogrammable, divertable, persistently divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: diverted
         6: Virtual Gesture Button    , default: Virtual Gesture Button      => Virtual Gesture Button    
             divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty
             reporting: default
     Battery: 90%, discharging, next level 50%.
```
</details>


**Is your feature request related to a problem? Please describe.**

**Describe the solution you'd like**
Would be nice to be able to configure simple mouse gestures (up/down/left/right) via rules editor in Solaar like you can in Logitech Options.

**Describe alternatives you've considered**
https://github.com/PixlOne/logiops can configure gestures (but w/o UI) for mouses that have separate special gesture on/off button. But not for mouses that don't have this button like mx vertical. Logitech Options uses Fn button from keyboard (e.g. mx keys). 
(As a side note - when gesture button is pressed mouse pointer freezes, which is convenient/safe.)

Anyway solutions like easystroke or built-in kde gestures do not allow to use keyboard as an initiator of mouse gesture (at least not in a way like Logitech Options does).

Thanks for hearing me out.

**Additional context**
![logitech-options-custom-gesture-preset](https://user-images.githubusercontent.com/17964763/110483853-2ae92080-80fb-11eb-8fa3-1b0fbf02bd10.png)
![duolink](https://user-images.githubusercontent.com/17964763/110483861-2de41100-80fb-11eb-9d93-fe8e7fe173f3.PNG)




Discussion:
My understanding is that Logitech Options communicates with the Logitech drivers to implement some of its functionality.   This goes beyond what Solaar can reasonably do so there are some things that can be done with Logitech Options that won't be part of Solaar.

It appears that you want to be able to press a keyboard key and then have mouse movement trigger gestures.  This is either at the very edge of what Solaar can reasonably do or beyond it.   I don't think that Solaar rules are capable of doing what you want, so some other mechanism would have to be added.

I see that the MX Vertical has a Virtual Gestures button.  How do mouse gestures work on the MX Vertical under Logitech Options?


Thanks for the reply.

I also noticed this "Virtual Gesture Button" but I dunno where it goes from since there is no such button on the mouse.

>Logitech Options uses Fn button from keyboard (e.g. mx keys).

so basically you can configure "classic" mouse buttons and if you have a logitech keyboard (like mx keys) you also can configure mouse buttons when Fn button on keyboard is pressed, and also new feature "gestures" appears - again works only if you press and hold Fn button on keyboard. Works on Windows and MacOS.

I think they call this feature [Duolink](https://www.logitech.com/en-us/product/options#duolink). But basically it is just emulating mouse gesture button via keyboard button.
Just for test i was able to make [logiops](https://github.com/PixlOne/logiops) to use top mouse button (DPI button) as the gesture button - works fine (but you loose 1 button on the mouse that have only few buttons). But this might mean than solaar at least can do the same (redefine any mouse button to be a gesture button). But still support of configuring gestures is needed.
Just for test it would be interesting to add "Virtual Gesture Button" as "Key press" option in rules.
Then I can try to map some keyboard key to this "Virtual Gesture Button" and see if it will trigger mouse's gesture mode.
(tbh, i tried this with logiops but with no luck, probably invoke of press of "Virtual Gesture Button" should be done somehow differently).

But even if this particular case will not work, it does not reduce value of "configure gestures" via rules.

(Also in logiops I was able to set "dpi button" to act as "gesture button" and then assign original action of "dpi button" to no-op (no movement/direction) action).

Thanks.

The idea behind virtual buttons is that they are targets for button reprogramming.   But on the MX Vertical it doesn't appear that they can be used because there are no buttons that can be reprogrammed to its group (group 3).   I'm trying to find out how to trigger its behaviour.

What the button does is to divert mouse movement events so that software can, if it chooses, do something special with the events.   This is what Solaar does for sliding DPI adjustment.   To handle gestures, the idea would be to revamp this code so that it could be used for both purposes.

Adding a Virtual Gesture Button to the rules wouldn't do anything, I think, as that would send the button press towards Linux.   Instead a message has to be sent to the device.
>Adding a Virtual Gesture Button to the rules wouldn't do anything, I think, as that would send the button press towards Linux. Instead a message has to be sent to the device.

that was my suspicion, thank you for confirmation.
Solaar isn't a device driver so it should (has to?) work somewhat differently than logiops, I think.  It may be that there is a way to send the Virtual Gesture Button operation to the mouse - I'm trying to find this out.
OK, with some help I now know how to get the mouse in the required state.

The next question is what the interface to the functionality should be.  Overloading the Fn key on the keyboard doesn't fit into the way that Solaar works.
Cool!
In the first iteration I think just reassigning any mouse button to be a gesture button is ok.
Then of course rules for gestures (direction=>action).
And one of the rules should be for no-op (no movement) so it can be assigned to the original button action.

About keyboard Fn button - maybe along the way it will be clear how to use it as well, maybe not.
Not a big deal.
For mx keys, for, example, the thing is that Logitech placed this button in not very convenient position to use in conjunction with mouse - near to right control, so I am not sure that this "duolink"  feature used very often on supported OSes.
While possibility to make, let's say, dpi button to act like a gesture button probably will be used by people.

Thank you.
There is a button on the MX Master 3 (see image above) that is actually supposed to be used for this.  I'm going to use this button in the first iteration.
I'm still working on implementation.
I think that the result will be that mouse gestures will feed into the Solaar rule system.
@pfps any updates on this ?
Still in progress.  Maybe half done.
I worked a bit on the code and I am now need to determine how the gesture is reported into the rule system.  I am currently creating a fake notification with a scaled x and y (so that tiny twitches are reported as no movement).   Does this make sense?

This currently only works for mice with gesture buttons.  I'm not sure how it could be extended to other mice.
>While possibility to make, let's say, dpi button to act like a gesture button probably will be used by people.

it this possible?

i mean assign "gesture button" to "dpi button"
There are several different things that can be done, depending on the device.

Some devices allow buttons to be assigned to the actions of other buttons.  So it might be possible to assign a button on your mouse to perform the action of the gesture button.  For example, the MX Master 3 Smart Shift button can be assigned to the Gesture action (by software).

Some devices have a virtual gesture button that can be triggered by software.   Both the MX Master 3 and the MX Vertical have these virtual buttons.

But all that is needed for mouse gestures is having a button that causes the device to send raw XY reports.  Many buttons can do this.  On the MX Vertical, the middle, back, forward, and DPI buttons all have this capability.

A major issue is how to set up an interface that enables mouse gestures in a semi-intuitive way.   The code that is going into Solaar doesn't handle any of theses situations for now.  It only works with an explicit gesture button, and ignores any button assignments.  (The DPI Sliding Adjustment doesn't take button assignments into account, I think.)

I should probably handle the first case, which can be done without any GUI changes.
EDIT:  On further thought this is probably a bad idea.
i see, thanks.
one note: would be nice if 
>when gesture button is pressed mouse pointer freezes, which is convenient/safe

like in Logitech Options and logiops
if possible, of course.
The mouse pointer already freezes when doing sliding DPI and mouse gestures.
@sidevesh 

See PR #1138 for the initial implementation of mouse gestures.   The PR still has debugging print functions in the code, which you can ignore.

Right now, movement is quantized, so the mouse has to move a few millimeters before movement is recorded.

If you have a mouse without the App_Switch_Gesture you can test out the the PR by changing App_Switch_Gesture in lib/logitech_receiver/settings.py (3 places) to the name of a button that has the divertable and raw XY capabilities.  You can find out the suitable buttons with `bin/solaar show`.  Note that spaces in the button name have to be turned into underscores.

Comments encouraged.
Tested the PR, my mouse m720 Triathlon doesn't have App_Switch_Gesture but does have MultiPlatform_Gesture_Button (button right below the thumb area, that Logi Options calls Gesture Button).
I replaced all mentions of `App_Switch_Gesture` with `MultiPlatform_Gesture_Button` in settings.py as well as settings_templates.py along with mouse-up, down rules but the gestures didn't work and pressing and holding the gesture button didn't stop the mouse from moving like it does in Logi Options.
I can confirm that the button has divertable and war xy capabilities and it is the correct button since I tested adding a rule on press of that button and that did work.
Please post output of `bin/solaar -dd` from startup to attempting a mouse gesture.

You need to turn the gesture setting on *and* divert the button for mouse gestures to work.
Nevermind, I made a typo in the change, with that fixed it works perfectly, I see the gestures option in the menu and the rules work.
Excellent.  Please try PR #1138 again.  It should work for your mouse without any changes.
Can confirm it works :+1:  Look good to go to me :)
Thanks for adding this in, looks like atleast for the keyboard, mouse I have, Solaar is now on par with Logi Options feature-wise (if not ahead). :partying_face: 
Addressed by #1138
@pfps 
hi.
so, does it already have any button remap feature to "Virtual Gesture Button" or it works only for limited set of buttons?
in readme i can see
`This processing is only set up in Solaar when the Mouse Gestures setting is on and the App Switch Gesture button or MultiPlatform Gesture Button is diverted.`
Virtual Gesture Button doesn't work (yet).  I don't see a good way to use keys on keyboards to initiate the gesture processing.
about keyboard button, as i said
>For mx keys, for, example, the thing is that Logitech placed this button in not very convenient position to use in conjunction with mouse - near to right control, so I am not sure that this "duolink" feature used very often on supported OSes.

so i guess we can skip it.

but as for `Virtual Gesture Button` - would be nice to have :-)

thank you.
There are two things that could be done.
1/ Use any button that has the raw XY and divertable capabilities as the triggering button.
2/ Use a keyboard key (Fn) to control the Virtual Gesture Button method.
It doesn't look like reprogramming a button as the Virtual Gesture Button is possible.

The problem with the first is how to set up the UI.
The problem with the second is how to get the right information to Solaar.

Suggestions welcome.
For 1) Maybe Key/Button Diversion menu item can have a third option `Diversion for gestures` or something similar if that button supports reporting xy and is divertable. Seems like the quickest way to expose that in UI.

For 2) not sure but I know that using Fn key for gestures (DuoLink) is only present in few keyboards, MX keys has it but K375s that Im using doesn't, maybe there is something with MX keys and other DuoLink supported keyboards that makes that possible that other keyboards don't.
@antonsoroko Please give PR #1148 a try
Hi @pfps.
Mouse Gestures option is there when using Unifying receiver, but not when using bluetooth connection. At least on M720. Why is that?
I have heard from Logitech that some mice have different features when connected via Bluetooth.   To check, run `solaar show` under both connections and attach output here.
Here it is. By the way with `logiops` gestures work fine over bluetooth.

<details>
<summary>Unifying</summary>

```
Unifying Receiver
  Device path  : /dev/hidraw1
  USB id       : 046d:C52B
  Serial       : 2E7E9484
    Firmware   : 12.11.B0032
    Bootloader : 04.16
    Other      : AA.AA
  Has 1 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 1=200

  1: M720 Triathlon Multi-Device Mouse
     Device path  : /dev/hidraw2
     WPID         : 405E
     Codename     : M720 Triathlon
     Kind         : mouse
     Protocol     : HID++ 4.5
     Polling rate : 8 ms (125Hz)
     Serial number: 4BD4CAEE
     Model ID:      B015405E0000
     Unit ID:       8127593F
        Bootloader: BOT 34.01.B0005
          Firmware: MPM 04.10.B0013
             Other: 
     The power switch is located on the base.
     Supports 36 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BOT 34.01.B0005 000000A96719
            Firmware: Firmware MPM 04.10.B0013 405E00A96719
            Firmware: Other   
            Unit ID: 8127593F  Model ID: B015405E0000  Transport IDs: {'btleid': 'B015', 'wpid': '405E'}
         3: DEVICE NAME            {0005}   
            Name: M720 Triathlon Multi-Device Mouse
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: CRYPTO ID              {0021}   
         7: DEVICE FRIENDLY NAME   {0007}   
         8: BATTERY STATUS         {1000}   
            Battery: 50%, discharging, next level 20%.
         9: CHANGE HOST            {1814}   
            Change Host        : 1:XPS
        10: HOSTS INFO             {1815}   
            Host 0 (paired): XPS
            Host 1 (paired): HUAWEI P30 Pro
            Host 2 (paired): MRX
        11: REPROG CONTROLS V4     {1B04}   
            Key/Button Actions (saved): {'208': 208, '80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93}
            Key/Button Actions        : {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208}
        12: PERSISTENT REMAPPABLE ACTION {1C00}   
        13: POINTER SPEED          {2205}   
            Pointer Speed: 1.0
            Sensitivity (Pointer Speed) (saved): 256
            Sensitivity (Pointer Speed)        : 256
        14: VERTICAL SCROLLING     {2100}   
            Roller type: 3G
            Ratchet per turn: 24
            Scroll lines: 0
        15: DFUCONTROL UNSIGNED    {00C1}   
        16: DFUCONTROL SIGNED      {00C2}   
        17: DEVICE RESET           {1802}   internal, hidden
        18: unknown:1803           {1803}   internal, hidden
        19: CONFIG DEVICE PROPS    {1806}   internal, hidden
        20: OOBSTATE               {1805}   internal, hidden
        21: unknown:1813           {1813}   internal, hidden
        22: unknown:1830           {1830}   internal, hidden
        23: unknown:1861           {1861}   internal, hidden
        24: unknown:1890           {1890}   internal, hidden
        25: unknown:1891           {1891}   internal, hidden
        26: unknown:18A1           {18A1}   internal, hidden
        27: unknown:1DF3           {1DF3}   internal, hidden
        28: unknown:1E00           {1E00}   hidden
        29: unknown:1EB0           {1EB0}   internal, hidden
        30: unknown:18B1           {18B1}   internal, hidden
        31: unknown:1850           {1850}   internal, hidden
        32: unknown:1E22           {1E22}   
        33: unknown:1F03           {1F03}   internal, hidden
        34: unknown:18C0           {18C0}   internal, hidden
        35: HIRES WHEEL            {2121}   
            Multiplier: 8
            Has invert: Normal wheel motion
            Has ratchet switch: Normal wheel mode
            Low resolution mode
            HID notification
            Scroll Wheel Direction (saved): False
            Scroll Wheel Direction        : False
            Scroll Wheel Resolution (saved): False
            Scroll Wheel Resolution        : False
     Has 9 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, reprogrammable, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, reprogrammable, pos:0, group:1, group mask:g1
             reporting: default
         2: Middle Button             , default: Mouse Middle Button         => Mouse Middle Button       
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         3: Back Button               , default: Mouse Back Button           => Mouse Back Button         
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         4: Forward Button            , default: Mouse Forward Button        => Mouse Forward Button      
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         5: Left Tilt                 , default: Mouse Scroll Left Button    => Mouse Scroll Left Button  
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         6: Right Tilt                , default: Mouse Scroll Right Button   => Mouse Scroll Right Button 
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         7: MultiPlatform Gesture Button, default: Multiplatform Gesture Button => Multiplatform Gesture Button
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: diverted, raw XY diverted
         8: Virtual Gesture Button    , default: Virtual Gesture Button      => Virtual Gesture Button    
             divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty
             reporting: default
```

</details>

<details>
<summary>Bluetooth</summary>

```
Wired Devices
  1: M720 Triathlon Multi-Device Mouse
     Device path  : /dev/hidraw3
     USB id       : 046d:B015
     Codename     : M720 Triathlon
     Kind         : mouse
     Protocol     : HID++ 4.5
     Serial number: ?
     Model ID:      B015405E0000
     Unit ID:       8127593F
        Bootloader: BOT 34.01.B0005
          Firmware: MPM 04.10.B0013
             Other: 
     Supports 31 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
            Firmware: Bootloader BOT 34.01.B0005 000000A96719
            Firmware: Firmware MPM 04.10.B0013 B01500A96719
            Firmware: Other   
            Unit ID: 8127593F  Model ID: B015405E0000  Transport IDs: {'btleid': 'B015', 'wpid': '405E'}
         3: DEVICE NAME            {0005}   
            Name: M720 Triathlon Multi-Device Mouse
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: CRYPTO ID              {0021}   
         7: DEVICE FRIENDLY NAME   {0007}   
         8: BATTERY STATUS         {1000}   
            Battery: 50%, discharging, next level 20%.
         9: CHANGE HOST            {1814}   
            Change Host        : 1:XPS
        10: HOSTS INFO             {1815}   
            Host 0 (paired): XPS
            Host 1 (paired): HUAWEI P30 Pro
            Host 2 (paired): MRX
        11: REPROG CONTROLS V4     {1B04}   
            Key/Button Actions (saved): {'208': 208, '80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93}
            Key/Button Actions        : {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208}
        12: PERSISTENT REMAPPABLE ACTION {1C00}   
        13: POINTER SPEED          {2205}   
            Pointer Speed: 1.0
            Sensitivity (Pointer Speed) (saved): 256
            Sensitivity (Pointer Speed)        : 256
        14: VERTICAL SCROLLING     {2100}   
            Roller type: 3G
            Ratchet per turn: 24
            Scroll lines: 0
        15: DFUCONTROL UNSIGNED    {00C1}   
        16: DFUCONTROL SIGNED      {00C2}   
        17: unknown:1803           {1803}   internal, hidden
        18: unknown:1813           {1813}   internal, hidden
        19: unknown:1830           {1830}   internal, hidden
        20: unknown:18A1           {18A1}   internal, hidden
        21: unknown:1DF3           {1DF3}   internal, hidden
        22: unknown:1E00           {1E00}   hidden
        23: unknown:1EB0           {1EB0}   internal, hidden
        24: unknown:1861           {1861}   internal, hidden
        25: unknown:18B1           {18B1}   internal, hidden
        26: unknown:1850           {1850}   internal, hidden
        27: unknown:1E22           {1E22}   
        28: unknown:1F03           {1F03}   internal, hidden
        29: unknown:18C0           {18C0}   internal, hidden
        30: HIRES WHEEL            {2121}   
            Multiplier: 8
            Has invert: Normal wheel motion
            Has ratchet switch: Normal wheel mode
            Low resolution mode
            HID notification
            Scroll Wheel Direction (saved): False
            Scroll Wheel Direction        : False
            Scroll Wheel Resolution (saved): False
            Scroll Wheel Resolution        : False
     Has 9 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, reprogrammable, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, reprogrammable, pos:0, group:1, group mask:g1
             reporting: default
         2: Middle Button             , default: Mouse Middle Button         => Mouse Middle Button       
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         3: Back Button               , default: Mouse Back Button           => Mouse Back Button         
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         4: Forward Button            , default: Mouse Forward Button        => Mouse Forward Button      
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         5: Left Tilt                 , default: Mouse Scroll Left Button    => Mouse Scroll Left Button  
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         6: Right Tilt                , default: Mouse Scroll Right Button   => Mouse Scroll Right Button 
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         7: MultiPlatform Gesture Button, default: Multiplatform Gesture Button => Multiplatform Gesture Button
             mse, reprogrammable, divertable, persistently divertable, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: diverted
         8: Virtual Gesture Button    , default: Virtual Gesture Button      => Virtual Gesture Button    
             divertable, virtual, raw XY, force raw XY, pos:0, group:3, group mask:empty
             reporting: default
```

</details>
Hmm.  Should work both ways.
Please post output of `solaar -dd` when mouse is on bluetooth from startup to main window appearance.
Here is debug info and also screenshots of main window with unifying receiver and bluetooth. 

Only "Mouse Gestures" option is missing, everything else is there. Rule for "MultiPlatform Gesture Button" also works, but only as click. Gesture rules don't work.

With Unifying receiver everything works perfectly. 

<details>
<summary>Debug</summary>

```
12:15:12,323     INFO [MainThread] root: language en_US (UTF-8), translations path /usr/share/locale
12:15:12,445    DEBUG [MainThread] solaar.ui.tray: using AppIndicator3
12:15:12,454     INFO [MainThread] solaar.upower: connected to system dbus, watching for suspend/resume events
12:15:12,479    DEBUG [MainThread] solaar.ui: startup registered=True, remote=False
12:15:12,480    DEBUG [AsyncUI] solaar.tasks: started
12:15:12,480     INFO [MainThread] solaar.ui.notify: starting desktop notifications
12:15:12,482    DEBUG [MainThread] solaar.ui.icons: sys.path[0] = /usr/bin
12:15:12,482    DEBUG [MainThread] solaar.ui.icons: looking for icons in /usr/icons
12:15:12,482    DEBUG [MainThread] solaar.ui.icons: looking for icons in /usr/share/solaar/icons
12:15:12,482    DEBUG [MainThread] solaar.ui.icons: icon theme paths: ['/usr/share/solaar/icons', '/home/goran/.local/share/icons', '/home/goran/.icons', '/home/goran/.local/share/flatpak/exports/share/icons', '/var/lib/flatpak/exports/share/icons', '/usr/local/share/icons', '/usr/share/icons', '/var/lib/snapd/desktop/icons', '/home/goran/.local/share/flatpak/exports/share/pixmaps', '/var/lib/flatpak/exports/share/pixmaps', '/usr/local/share/pixmaps', '/usr/share/pixmaps', '/var/lib/snapd/desktop/pixmaps']
12:15:12,508     INFO [MainThread] solaar.listener: starting receiver listening threads
12:15:12,515     INFO [MainThread] solaar.listener: receiver event add DeviceInfo(path='/dev/hidraw2', vendor_id='046D', product_id='B015', serial='e3:d1:14:df:8b:f1', release=None, manufacturer=None, product=None, interface=None, driver='hid-generic', bus_id=5, isDevice=True)
12:15:12,516    DEBUG [MainThread] logitech_receiver.base: (19) pinging device 0
12:15:12,516    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 001A 00001500000000000000000000000000]
12:15:12,781    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 001A 04051500000000000000000000000000]
12:15:12,781    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000A 00010000000000000000000000000000]
12:15:12,797    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000A 01000100000000000000000000000000]
12:15:12,797    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0108 00000000000000000000000000000000]
12:15:12,818    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0108 1E000000000000000000000000000000]
12:15:12,819    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 00050000000000000000000000000000]
12:15:12,834    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 03000000000000000000000000000000]
12:15:12,834    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 032B 00000000000000000000000000000000]
12:15:12,849    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 032B 03000000000000000000000000000000]
12:15:12,849    DEBUG [MainThread] solaar.configuration: load => {}
12:15:12,850    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0009 00030000000000000000000000000000]
12:15:12,871    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0009 02000200000000000000000000000000]
12:15:12,871    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 020B 00000000000000000000000000000000]
12:15:12,886    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 020B 038127593F0006B015405E0000000000]
12:15:12,887    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 030D 00000000000000000000000000000000]
12:15:12,902    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 030D 21000000000000000000000000000000]
12:15:12,902    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 031E 00000000000000000000000000000000]
12:15:12,923    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 031E 4D373230205472696174686C6F6E204D]
12:15:12,924    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 031A 10000000000000000000000000000000]
12:15:12,939    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 031A 756C74692D446576696365204D6F7573]
12:15:12,939    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 031A 20000000000000000000000000000000]
12:15:12,954    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 031A 65000000000000000000000000000000]
12:15:12,955     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6'} to /home/goran/.config/solaar/config.json
12:15:12,956     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6'} to /home/goran/.config/solaar/config.json
12:15:12,956     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6'} to /home/goran/.config/solaar/config.json
12:15:12,956    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000C 21200000000000000000000000000000]
12:15:12,969    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000C 00000000000000000000000000000000]
12:15:12,969    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000A 21300000000000000000000000000000]
12:15:12,991    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000A 00000000000000000000000000000000]
12:15:12,991    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000C 21210000000000000000000000000000]
12:15:13,006    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000C 1E000000000000000000000000000000]
12:15:13,007    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000E 00070000000000000000000000000000]
12:15:13,021    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000E 07000000000000000000000000000000]
12:15:13,022    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 070F 00000000000000000000000000000000]
12:15:13,043    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 070F 0E120E00000000000000000000000000]
12:15:13,044    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 071C 00000000000000000000000000000000]
12:15:13,059    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 071C 004D373230205472696174686C6F6E00]
12:15:13,007    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[HIRES WHEEL] detected <Setting([feature:toggle] M720 Triathlon:hires-smooth-invert=None)>
12:15:13,059    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[HIRES WHEEL] detected <Setting([feature:toggle] M720 Triathlon:hires-smooth-resolution=None)>
12:15:13,060    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000B 21100000000000000000000000000000]
12:15:13,074    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000B 00000000000000000000000000000000]
12:15:13,074    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000B 21110000000000000000000000000000]
12:15:13,096    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000B 00000000000000000000000000000000]
12:15:13,096    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0009 21500000000000000000000000000000]
12:15:13,111    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0009 00000000000000000000000000000000]
12:15:13,112    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000B 22010000000000000000000000000000]
12:15:13,126    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000B 00000000000000000000000000000000]
12:15:13,127    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 1B040000000000000000000000000000]
12:15:13,148    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 0B000300000000000000000000000000]
12:15:13,149    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected None
12:15:13,149    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected None
12:15:13,150    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000E 22050000000000000000000000000000]
12:15:13,164    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000E 0D000000000000000000000000000000]
12:15:13,164    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[POINTER SPEED] detected <Setting([feature:range] M720 Triathlon:pointer_speed=None)>
12:15:13,165    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000F 19820000000000000000000000000000]
12:15:13,179    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000F 00000000000000000000000000000000]
12:15:13,179    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 40A00000000000000000000000000000]
12:15:13,201    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 00000000000000000000000000000000]
12:15:13,201    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0009 40A20000000000000000000000000000]
12:15:13,216    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0009 00000000000000000000000000000000]
12:15:13,217    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 40A30000000000000000000000000000]
12:15:13,231    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 00000000000000000000000000000000]
12:15:13,232    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 1B000000000000000000000000000000]
12:15:13,253    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 00000000000000000000000000000000]
12:15:13,254    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B08 00000000000000000000000000000000]
12:15:13,269    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B08 09000000000000000000000000000000]
12:15:13,269    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1F 00000000000000000000000000000000]
12:15:13,284    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1F 00500038110001010000000000000000]
12:15:13,284    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1F 01000000000000000000000000000000]
12:15:13,306    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1F 00510039110001010000000000000000]
12:15:13,306    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1E 02000000000000000000000000000000]
12:15:13,321    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1E 0052003A710002030100000000000000]
12:15:13,322    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B19 03000000000000000000000000000000]
12:15:13,336    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B19 0053003C710002030100000000000000]
12:15:13,337    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1F 04000000000000000000000000000000]
12:15:13,358    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1F 0056003E710002030100000000000000]
12:15:13,359    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1A 05000000000000000000000000000000]
12:15:13,374    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1A 005B003F710002030100000000000000]
12:15:13,375    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B19 06000000000000000000000000000000]
12:15:13,389    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B19 005D0040710002030100000000000000]
12:15:13,389    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B18 07000000000000000000000000000000]
12:15:13,404    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B18 00D000AD710002030100000000000000]
12:15:13,404    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B1A 08000000000000000000000000000000]
12:15:13,426    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B1A 00D700B4A00003000300000000000000]
12:15:13,428    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected <Setting([feature:map choice] M720 Triathlon:reprogrammable-keys=None)>
12:15:13,430    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected <Setting([feature:map choice] M720 Triathlon:divert-keys=None)>
12:15:13,430    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 45210000000000000000000000000000]
12:15:13,441    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 00000000000000000000000000000000]
12:15:13,442    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000B 80600000000000000000000000000000]
12:15:13,456    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000B 00000000000000000000000000000000]
12:15:13,457    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000E 46000000000000000000000000000000]
12:15:13,478    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000E 00000000000000000000000000000000]
12:15:13,479    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000E 80100000000000000000000000000000]
12:15:13,494    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000E 00000000000000000000000000000000]
12:15:13,494    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000B 45310000000000000000000000000000]
12:15:13,509    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000B 00000000000000000000000000000000]
12:15:13,509    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000D 45300000000000000000000000000000]
12:15:13,531    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000D 00000000000000000000000000000000]
12:15:13,531    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0008 18140000000000000000000000000000]
12:15:13,546    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0008 09000100000000000000000000000000]
12:15:13,547    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 090E 00000000000000000000000000000000]
12:15:13,561    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 090E 03000000000000000000000000000000]
12:15:13,562    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000F 18150000000000000000000000000000]
12:15:13,583    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000F 0A000100000000000000000000000000]
12:15:13,584    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A0D 00000000000000000000000000000000]
12:15:13,599    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A0D 13090300000000000000000000000000]
12:15:13,599    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A19 00000000000000000000000000000000]
12:15:13,621    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A19 00010402031800000000000000000000]
12:15:13,621    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A3F 00000000000000000000000000000000]
12:15:13,643    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A3F 00005850530000000000000000000000]
12:15:13,643    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A1E 01000000000000000000000000000000]
12:15:13,666    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A1E 010104000E1800000000000000000000]
12:15:13,666    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A3D 01000000000000000000000000000000]
12:15:13,688    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A3D 0100485541574549205033302050726F]
12:15:13,688    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A1B 02000000000000000000000000000000]
12:15:13,704    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A1B 02010402031800000000000000000000]
12:15:13,704    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0A3A 02000000000000000000000000000000]
12:15:13,719    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0A3A 02004D52580000000000000000000000]
12:15:13,719    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[CHANGE HOST] detected <Setting([feature:choice] M720 Triathlon:change-host=None)>
12:15:13,719    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000C 65010000000000000000000000000000]
12:15:13,741    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000C 00000000000000000000000000000000]
12:15:13,741     INFO [MainThread] logitech_receiver.status: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> pushing device settings [<Setting([feature:toggle] M720 Triathlon:hires-smooth-invert=None)>, <Setting([feature:toggle] M720 Triathlon:hires-smooth-resolution=None)>, <Setting([feature:range] M720 Triathlon:pointer_speed=None)>, <Setting([feature:map choice] M720 Triathlon:reprogrammable-keys=None)>, <Setting([feature:map choice] M720 Triathlon:divert-keys=None)>, <Setting([feature:choice] M720 Triathlon:change-host=None)>]
12:15:13,741    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-invert: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:13,741    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-invert: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,741    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 1E1E 00000000000000000000000000000000]
12:15:13,756    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 1E1E 00000000000000000000000000000000]
12:15:13,756    DEBUG [MainThread] logitech_receiver.settings: BooleanValidator: validate read b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' => 00
12:15:13,757     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6', 'B015405E0000:8127593F': {'_name': 'M720 Triathlon Multi-Device Mouse', '_modelId': 'B015405E0000', '_unitId': '8127593F', 'hires-smooth-invert': False}} to /home/goran/.config/solaar/config.json
12:15:13,757    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-invert: settings write False to <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,757    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 1E1E 00000000000000000000000000000000]
12:15:13,771    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 1E1E 00000000000000000000000000000000]
12:15:13,771    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-resolution: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:13,771    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-resolution: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,772    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 1E18 00000000000000000000000000000000]
12:15:13,794    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 1E18 00000000000000000000000000000000]
12:15:13,794    DEBUG [MainThread] logitech_receiver.settings: BooleanValidator: validate read b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' => 00
12:15:13,794     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6', 'B015405E0000:8127593F': {'_name': 'M720 Triathlon Multi-Device Mouse', '_modelId': 'B015405E0000', '_unitId': '8127593F', 'hires-smooth-invert': False, 'hires-smooth-resolution': False}} to /home/goran/.config/solaar/config.json
12:15:13,795    DEBUG [MainThread] logitech_receiver.settings: hires-smooth-resolution: settings write False to <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,795    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 1E18 00000000000000000000000000000000]
12:15:13,809    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 1E18 00000000000000000000000000000000]
12:15:13,809    DEBUG [MainThread] logitech_receiver.settings: pointer_speed: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:13,809    DEBUG [MainThread] logitech_receiver.settings: pointer_speed: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,810    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0D0C 00000000000000000000000000000000]
12:15:13,824    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0D0C 01000000000000000000000000000000]
12:15:13,825     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6', 'B015405E0000:8127593F': {'_name': 'M720 Triathlon Multi-Device Mouse', '_modelId': 'B015405E0000', '_unitId': '8127593F', 'hires-smooth-invert': False, 'hires-smooth-resolution': False, 'pointer_speed': 256}} to /home/goran/.config/solaar/config.json
12:15:13,825    DEBUG [MainThread] logitech_receiver.settings: pointer_speed: settings write 256 to <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,825    DEBUG [MainThread] logitech_receiver.settings: pointer_speed: settings prepare write(256) => b'\x01\x00'
12:15:13,825    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0D1C 01000000000000000000000000000000]
12:15:13,839    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0D1C 01000000000000000000000000000000]
12:15:13,839    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:13,839    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,840    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2C 00500000000000000000000000000000]
12:15:13,861    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2C 00500000500000000000000000000000]
12:15:13,861    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2D 00510000000000000000000000000000]
12:15:13,876    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2D 00510000510000000000000000000000]
12:15:13,877    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2F 00520000000000000000000000000000]
12:15:13,892    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2F 00520000520000000000000000000000]
12:15:13,892    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B28 00530000000000000000000000000000]
12:15:13,913    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B28 00530000530000000000000000000000]
12:15:13,914    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2D 00560000000000000000000000000000]
12:15:13,929    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2D 00560000560000000000000000000000]
12:15:13,929    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2C 005B0000000000000000000000000000]
12:15:13,944    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2C 005B01005B0000000000000000000000]
12:15:13,944    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2F 005D0000000000000000000000000000]
12:15:13,966    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2F 005D01005D0000000000000000000000]
12:15:13,966    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2B 00D00000000000000000000000000000]
12:15:13,981    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2B 00D00100D00000000000000000000000]
12:15:13,982     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6', 'B015405E0000:8127593F': {'_name': 'M720 Triathlon Multi-Device Mouse', '_modelId': 'B015405E0000', '_unitId': '8127593F', 'hires-smooth-invert': False, 'hires-smooth-resolution': False, 'pointer_speed': 256, 'reprogrammable-keys': {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208}}} to /home/goran/.config/solaar/config.json
12:15:13,983    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings write {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208} to <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:13,983    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(80,80) => b'\x00\x00P'
12:15:13,983    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3E 00500000500000000000000000000000]
12:15:13,996    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3E 00000000000000000000000000000000]
12:15:13,997    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(81,81) => b'\x00\x00Q'
12:15:13,997    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B38 00510000510000000000000000000000]
12:15:14,018    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B38 00000000000000000000000000000000]
12:15:14,019    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(82,82) => b'\x00\x00R'
12:15:14,019    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3C 00520000520000000000000000000000]
12:15:14,034    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3C 00000000000000000000000000000000]
12:15:14,034    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(83,83) => b'\x00\x00S'
12:15:14,034    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3F 00530000530000000000000000000000]
12:15:14,049    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3F 00000000000000000000000000000000]
12:15:14,049    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(86,86) => b'\x00\x00V'
12:15:14,050    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B38 00560000560000000000000000000000]
12:15:14,071    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B38 00000000000000000000000000000000]
12:15:14,071    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(91,91) => b'\x00\x00['
12:15:14,072    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3C 005B00005B0000000000000000000000]
12:15:14,087    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3C 00000000000000000000000000000000]
12:15:14,087    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(93,93) => b'\x00\x00]'
12:15:14,087    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B39 005D00005D0000000000000000000000]
12:15:14,101    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B39 00000000000000000000000000000000]
12:15:14,102    DEBUG [MainThread] logitech_receiver.settings: reprogrammable-keys: settings prepare map write(208,208) => b'\x00\x00\xd0'
12:15:14,102    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3E 00D00000D00000000000000000000000]
12:15:14,123    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3E 00000000000000000000000000000000]
12:15:14,124    DEBUG [MainThread] logitech_receiver.settings: divert-keys: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:14,124    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,124    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2D 00520000000000000000000000000000]
12:15:14,139    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2D 00520000520000000000000000000000]
12:15:14,139    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2A 00530000000000000000000000000000]
12:15:14,154    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2A 00530000530000000000000000000000]
12:15:14,154    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B28 00560000000000000000000000000000]
12:15:14,176    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B28 00560000560000000000000000000000]
12:15:14,176    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2E 005B0000000000000000000000000000]
12:15:14,192    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2E 005B01005B0000000000000000000000]
12:15:14,192    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B28 005D0000000000000000000000000000]
12:15:14,206    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B28 005D01005D0000000000000000000000]
12:15:14,207    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B29 00D00000000000000000000000000000]
12:15:14,229    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B29 00D00100D00000000000000000000000]
12:15:14,230     INFO [MainThread] solaar.configuration: saved {'_version': '1.0.6', 'B015405E0000:8127593F': {'_name': 'M720 Triathlon Multi-Device Mouse', '_modelId': 'B015405E0000', '_unitId': '8127593F', 'hires-smooth-invert': False, 'hires-smooth-resolution': False, 'pointer_speed': 256, 'reprogrammable-keys': {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208}, 'divert-keys': {'82': 0, '83': 0, '86': 0, '91': 0, '93': 0, '208': 0}}} to /home/goran/.config/solaar/config.json
12:15:14,230    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings write {'82': 0, '83': 0, '86': 0, '91': 0, '93': 0, '208': 0} to <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,230    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(82,0) => b'\x00'
12:15:14,230    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3F 00520200000000000000000000000000]
12:15:14,244    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3F 00000000000000000000000000000000]
12:15:14,244  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,245    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2A 00520000000000000000000000000000]
12:15:14,259    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2A 00520000520000000000000000000000]
12:15:14,259    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(83,0) => b'\x00'
12:15:14,260    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B39 00530200000000000000000000000000]
12:15:14,281    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B39 00000000000000000000000000000000]
12:15:14,281  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,282    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B29 00530000000000000000000000000000]
12:15:14,297    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B29 00530000530000000000000000000000]
12:15:14,297    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(86,0) => b'\x00'
12:15:14,297    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B39 00560200000000000000000000000000]
12:15:14,311    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B39 00000000000000000000000000000000]
12:15:14,312  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,312    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2E 00560000000000000000000000000000]
12:15:14,326    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2E 00560000560000000000000000000000]
12:15:14,327    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(91,0) => b'\x00'
12:15:14,327    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3D 005B0200000000000000000000000000]
12:15:14,348    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3D 00000000000000000000000000000000]
12:15:14,349  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,349    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2F 005B0000000000000000000000000000]
12:15:14,364    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2F 005B00005B0000000000000000000000]
12:15:14,364    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(93,0) => b'\x00'
12:15:14,365    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3C 005D0200000000000000000000000000]
12:15:14,379    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3C 00000000000000000000000000000000]
12:15:14,379  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,379    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B29 005D0000000000000000000000000000]
12:15:14,401    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B29 005D00005D0000000000000000000000]
12:15:14,401    DEBUG [MainThread] logitech_receiver.settings: divert-keys: settings prepare map write(208,0) => b'\x00'
12:15:14,402    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B3A 00D00200000000000000000000000000]
12:15:14,416    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B3A 00000000000000000000000000000000]
12:15:14,417  WARNING [MainThread] logitech_receiver.hidpp20: REPROG_CONTROLS_v4 endpoint setCidReporting on device <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> should echo request packet, but didn't.
12:15:14,417    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0B2A 00D00000000000000000000000000000]
12:15:14,431    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0B2A 00D00000D00000000000000000000000]
12:15:14,432    DEBUG [MainThread] logitech_receiver.settings: change-host: apply None (<Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>)
12:15:14,432    DEBUG [MainThread] logitech_receiver.settings: change-host: settings read None from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,432    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 090C 00000000000000000000000000000000]
12:15:14,453    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 090C 03000000000000000000000000000000]
12:15:14,454    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 000C 10000000000000000000000000000000]
12:15:14,469    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 000C 08000000000000000000000000000000]
12:15:14,469    DEBUG [MainThread] logitech_receiver.base: (19) <= w[11 00 0809 00000000000000000000000000000000]
12:15:14,484    DEBUG [MainThread] logitech_receiver.base: (19) => r[11 00 0809 32140000000000000000000000000000]
12:15:14,484    DEBUG [MainThread] logitech_receiver.hidpp20: device 0 battery 50% charged, next 20%, status discharging
12:15:14,485    DEBUG [MainThread] logitech_receiver.status: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>: battery 50, discharging
12:15:14,485     INFO [MainThread] solaar.listener: status_changed <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>: paired online, {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
12:15:14,485     INFO [MainThread] solaar.listener: status_changed <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>: paired online, {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
12:15:14,486     INFO [ReceiverListener:hidraw2] logitech_receiver.listener: started with <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> (19)
12:15:14,486     INFO [ReceiverListener:hidraw2] solaar.listener: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>: notifications listener has started (19)
12:15:14,486     INFO [ReceiverListener:hidraw2] solaar.listener: status_changed <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>: paired online, {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None, 'NOTIFICATION FLAGS': False} (0) 
12:15:14,487    DEBUG [MainThread] solaar.ui: activate
12:15:14,490    DEBUG [MainThread] solaar.ui: status changed: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> (NONE) None
12:15:14,492    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,492    DEBUG [MainThread] solaar.ui.tray: picked device with lowest battery: ('/dev/hidraw2', 0, 'M720 Triathlon Multi-Device Mouse', {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None, 'NOTIFICATION FLAGS': False})
12:15:14,493    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,495    DEBUG [MainThread] solaar.ui.window: new device row ('/dev/hidraw2', 0, True, 'M720 Triathlon', 'input-mouse', None, None, <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>) at index 0
12:15:14,496    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,497    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,503    DEBUG [AsyncUI] logitech_receiver.settings: hires-smooth-invert: settings read False from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,505    DEBUG [AsyncUI] logitech_receiver.settings: hires-smooth-resolution: settings read False from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,509    DEBUG [AsyncUI] logitech_receiver.settings: pointer_speed: settings read 256 from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,518    DEBUG [AsyncUI] logitech_receiver.settings: reprogrammable-keys: settings read {'80': 80, '81': 81, '82': 82, '83': 83, '86': 86, '91': 91, '93': 93, '208': 208} from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,522    DEBUG [AsyncUI] logitech_receiver.settings: divert-keys: settings read {'82': 0, '83': 0, '86': 0, '91': 0, '93': 0, '208': 0} from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,524    DEBUG [AsyncUI] logitech_receiver.settings: change-host: settings read NamedInt(0, '1:XPS') from <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)>
12:15:14,528    DEBUG [MainThread] solaar.ui: status changed: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> (NONE) None
12:15:14,528    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,528    DEBUG [MainThread] solaar.ui.tray: picked device with lowest battery: ('/dev/hidraw2', 0, 'M720 Triathlon Multi-Device Mouse', {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None, 'NOTIFICATION FLAGS': False})
12:15:14,529    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,529    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,529    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,530    DEBUG [MainThread] solaar.ui: status changed: <Device(0,B015,M720 Triathlon Multi-Device Mouse,?)> (NONE) None
12:15:14,530    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,530    DEBUG [MainThread] solaar.ui.tray: picked device with lowest battery: ('/dev/hidraw2', 0, 'M720 Triathlon Multi-Device Mouse', {'BATTERY LEVEL': 50, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 20, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None, 'NOTIFICATION FLAGS': False})
12:15:14,530    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,531    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
12:15:14,531    DEBUG [MainThread] solaar.ui.icons: battery icon for 50:False = battery-good
```

</details>

![unifying](https://user-images.githubusercontent.com/12777776/120629146-cde4d880-c465-11eb-99e5-94c5a609f975.png)
![bluetooth](https://user-images.githubusercontent.com/12777776/120629161-d0dfc900-c465-11eb-9586-606deb8644bb.png)

I can't figure out what is going wrong, so please clone this repository and patch this function in lib/logitech_receiver/settings_templates.py 

```
def _feature_mouse_gesture_callback(device):
    # need a gesture button that can send raw XY
    if device.kind == _DK.mouse:
        print("MGC")
        keys = []
        for key in _MouseGestureKeys:
            key_index = device.keys.index(key)
            dkey = device.keys[key_index] if key_index is not None else None
            print("MGC key", key)
            if dkey is not None and 'raw XY' in dkey.flags and 'divertable' in dkey.flags:
                print("MGC key", key, list(dkey.flags))
                keys.append(dkey.key)
        print("MGC keys", keys)
        if not keys:  # none of the keys designed for this, so look for any key with correct flags
            for key in device.keys:
                print("MGC", key, list(key.flags))
                if 'raw XY' in key.flags and 'divertable' in key.flags and 'virtual' not in key.flags:
                    print("MGC", key, list(key.flags))
                    keys.append(key.key)
        print("MGC keys", keys)
        if keys:
            keys.insert(0, _NamedInt(0, 'Off'))
            print("MOUSE GESTURE CALLBACK", _NamedInts.list(keys))
            return _ChoicesV(_NamedInts.list(keys), byte_count=2)
```

Then kill Solaar and run `bin/solaar`.  Do this on bluetooth and not on bluetooth.  Post the output.  Thanks.
Here is part of the log. But I found out that this condition `device.kind == _DK.mouse` in callback function is not `true` for bluetooth and both values return `mouse`. It's `true` with receiver. Also if I remove that condition, option is there and works perfectly. Converting those values to strings or changing `_DK = _hidpp10.DEVICE_KIND` to `_DK = _hidpp20.DEVICE_KIND` fixes issue. This is first time I am looking into this lib and working with python so I don't know what's the best way. 

<details>
<summary>Receiver</summary>

```
09:04:34,754    DEBUG [ReceiverListener:hidraw1] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected None
MGC
09:04:34,755    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0008 1B000000000000000000000000000000]
09:04:34,774    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0008 00000000000000000000000000000000]
09:04:34,774    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B0A 00000000000000000000000000000000]
09:04:34,794    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B0A 09000000000000000000000000000000]
09:04:34,794    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1F 00000000000000000000000000000000]
09:04:34,814    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1F 00500038110001010000000000000000]
09:04:34,814    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1C 01000000000000000000000000000000]
09:04:34,836    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1C 00510039110001010000000000000000]
09:04:34,836    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1C 02000000000000000000000000000000]
09:04:34,854    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1C 0052003A710002030100000000000000]
09:04:34,854    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B19 03000000000000000000000000000000]
09:04:34,876    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B19 0053003C710002030100000000000000]
09:04:34,876    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1A 04000000000000000000000000000000]
09:04:34,896    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1A 0056003E710002030100000000000000]
09:04:34,896    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1F 05000000000000000000000000000000]
09:04:34,916    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1F 005B003F710002030100000000000000]
09:04:34,916    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1D 06000000000000000000000000000000]
09:04:34,936    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1D 005D0040710002030100000000000000]
09:04:34,936    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1A 07000000000000000000000000000000]
09:04:34,956    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1A 00D000AD710002030100000000000000]
09:04:34,956    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) <= w[11 01 0B1B 08000000000000000000000000000000]
09:04:34,976    DEBUG [ReceiverListener:hidraw1] logitech_receiver.base: (19) => r[11 01 0B1B 00D700B4A00003000300000000000000]
MGC key Mouse Gesture Button
MGC key MultiPlatform Gesture Button
MGC key MultiPlatform Gesture Button ['mse', 'reprogrammable', 'divertable', 'persistently divertable', 'raw XY']
MGC keys [NamedInt(208, 'MultiPlatform Gesture Button')]
MGC keys [NamedInt(208, 'MultiPlatform Gesture Button')]
MOUSE GESTURE CALLBACK NamedInts(NamedInt(0, 'Off'), NamedInt(208, 'MultiPlatform Gesture Button'))
09:04:34,977    DEBUG [ReceiverListener:hidraw1] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected <Setting([feature:choice] M720 Triathlon:mouse-gestures=None)>
```

</details>

<details>
<summary>Bluetooth</summary>

```
09:08:53,959    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected None
09:08:53,959    DEBUG [MainThread] logitech_receiver.settings_templates: check_feature[REPROG CONTROLS V4] detected None
```

</details>
"But I found out that this condition device.kind == _DK.mouse in callback function is not true for bluetooth and both values return mouse. It's true with receiver. Also if I remove that condition, option is there and works perfectly. Converting those values to strings or changing _DK = _hidpp10.DEVICE_KIND to _DK = _hidpp20.DEVICE_KIND fixes issue. This is first time I am looking into this lib and working with python so I don't know what's the best way."

That's the problem, but why is this happening?  Working on the Solaar code is not for the faint of heart - Solaar uses (I think overuses) a lot of tricky Python features.

What you can do is insert 
```
    print("MGC", device.kind, _DK.mouse, repr(device.kind), repr(_DK.mouse), device.kind == _DK.mouse)
```
before the device.kind test.   That will show what the actual data structures are.

I expect that there is some subtle problem elsewhere.  Or maybe your mouse is returning an unusual value for its kind.
I have been able to reproduce the problem locally.  The problem stems from how the kind is determined.  Once upon a time Solaar only worked with devices connected through a receiver and determined the kind from information the receiver stored.  Then Solaar only worked with bluetooth-connected devices that had a description in descriptors.py and for these devices determined the kind from information in that file.  Now Solaar works with just about any Logitech device that is recognized as a device that uses the HID++ protocol.   For these newly-recognizable devices Solaar determines the kind from an HID++ 2.0 message, which results in different kinds (but with the same string representation).  I'll put in a patch to fix this.

Resolving patch:
diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py
--- a/lib/logitech_receiver/device.py
+++ b/lib/logitech_receiver/device.py
@@ -23,6 +23,8 @@
 _R = _hidpp10.REGISTERS
 _IR = _hidpp10.INFO_SUBREGISTERS
 
+KIND_MAP = {kind: _hidpp10.DEVICE_KIND[str(kind)] for kind in _hidpp20.DEVICE_KIND}
+
 #
 #
 #
@@ -256,7 +258,7 @@ def kind(self):
                 kind = ord(pair_info[7:8]) & 0x0F
                 self._kind = _hidpp10.DEVICE_KIND[kind]
             elif self.online and self.protocol >= 2.0:
-                self._kind = _hidpp20.get_kind(self)
+                self._kind = KIND_MAP[_hidpp20.get_kind(self)]
         return self._kind or '?'
 
     @property
diff --git a/lib/logitech_receiver/hidpp10.py b/lib/logitech_receiver/hidpp10.py
--- a/lib/logitech_receiver/hidpp10.py
+++ b/lib/logitech_receiver/hidpp10.py
@@ -37,7 +37,17 @@
 # documentation, some of them guessed.
 #
 
-DEVICE_KIND = _NamedInts(keyboard=0x01, mouse=0x02, numpad=0x03, presenter=0x04, remote=0x07, trackball=0x08, touchpad=0x09)
+DEVICE_KIND = _NamedInts(
+    keyboard=0x01,
+    mouse=0x02,
+    numpad=0x03,
+    presenter=0x04,
+    remote=0x07,
+    trackball=0x08,
+    touchpad=0x09,
+    remote_control=0x0E,  # for compatability with HID++ 2.0
+    receiver=0x0F  # for compatability with HID++ 2.0
+)
 
 POWER_SWITCH_LOCATION = _NamedInts(
     base=0x01,


Test patch: