Repository: pwr-Solaar/Solaar

Issue:
Ratchet setting not remembered after resume from sleep
**Information**
- Solaar version: 1.1.5
- Distribution: Fedora 35
- Kernel version: Linux 5.19.16-100.fc35.x86_64 x86_64 GNU/Linux
- Output of `solaar show`:

<details>

```
Solaar version 1.1.5

Unifying Receiver
  Device path  : /dev/hidraw0
  USB id       : 046d:C52B
  Serial       : 428993A0
    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: 2=32

  1: ERGO M575
     Device path  : /dev/hidraw1
     WPID         : 4096
     Codename     : ERGO M575
     Kind         : mouse
     Protocol     : unknown (device is offline)
     Polling rate : 8 ms (125Hz)
     Serial number: BC4AE425
     The power switch is located on the base.
     Battery: unknown (device is offline).

  2: Wireless Mouse MX Master
     Device path  : /dev/hidraw2
     WPID         : 4071
     Codename     : MX Master
     Kind         : mouse
     Protocol     : HID++ 4.5
     Polling rate : 8 ms (125Hz)
     Serial number: 13CDB00E
     Model ID:      B01E40710000
     Unit ID:       5D96335E
        Bootloader: BOT 56.10.B0005
          Firmware: MPM 12.10.B0005
          Firmware: MPM 12.10.B0005
             Other: 
     The power switch is located on the base.
     Supports 31 HID++ 2.0 features:
         0: ROOT                   {0000} V0     
         1: FEATURE SET            {0001} V0     
         2: DEVICE FW VERSION      {0003} V2     
            Firmware: Bootloader BOT 56.10.B0005 40717622661101
            Firmware: Firmware MPM 12.10.B0005 B01E7622661101
            Firmware: Firmware MPM 12.10.B0005 40717622661101
            Firmware: Other   
            Unit ID: 5D96335E  Model ID: B01E40710000  Transport IDs: {'btleid': 'B01E', 'wpid': '4071'}
         3: DEVICE NAME            {0005} V0     
            Name: Wireless Mouse MX Master
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B} V0     
         5: RESET                  {0020} V0     
         6: CRYPTO ID              {0021} V0     
         7: BATTERY STATUS         {1000} V0     
            Battery: 20%, discharging, next level 5%.
         8: CONFIG DEVICE PROPS    {1806} V0    internal, hidden 
         9: CHANGE HOST            {1814} V1     
            Change Host        : 1:earth
        10: REPROG CONTROLS V4     {1B04} V3     
            Key/Button Actions (saved): {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift}
            Key/Button Actions        : {Left Button:Left Click, Right Button:Right Click, Middle Button:Mouse Middle Button, Back Button:Mouse Back Button, Forward Button:Mouse Forward Button, Mouse Gesture Button:Gesture Button Navigation, Smart Shift:Smart Shift}
            Key/Button Diversion (saved): {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular}
            Key/Button Diversion        : {Middle Button:Regular, Back Button:Regular, Forward Button:Regular, Mouse Gesture Button:Regular, Smart Shift:Regular}
        11: ADJUSTABLE DPI         {2201} V1     
            Sensitivity (DPI) (saved): 1400
            Sensitivity (DPI)        : 1400
        12: VERTICAL SCROLLING     {2100} V0     
            Roller type: 3G
            Ratchet per turn: 24
            Scroll lines: 0
        13: SMART SHIFT            {2110} V0     
            Scroll Wheel Rachet (saved): 20
            Scroll Wheel Rachet        : 20
        14: HIRES WHEEL            {2121} V0     
            Multiplier: 8
            Has invert: Normal wheel motion
            Has ratchet switch: Normal wheel mode
            High resolution mode
            HID notification
            Scroll Wheel Direction (saved): False
            Scroll Wheel Direction        : False
            Scroll Wheel Resolution (saved): True
            Scroll Wheel Resolution        : True
            Scroll Wheel Diversion (saved): False
            Scroll Wheel Diversion        : False
        15: GESTURE 2              {6501} V0     
            Gestures (saved): {45: True, 46: True}
            Gestures        : {46: True, 45: True}
            Gestures Diversion (saved): {46: False}
            Gestures Diversion        : {46: False}
        16: DFUCONTROL SIGNED      {00C2} V0     
        17: unknown:1813           {1813} V0    internal, hidden 
        18: unknown:1830           {1830} V0    internal, hidden 
        19: unknown:1890           {1890} V0    internal, hidden 
        20: unknown:1891           {1891} V0    internal, hidden 
        21: unknown:18A1           {18A1} V0    internal, hidden 
        22: unknown:18C0           {18C0} V0    internal, hidden 
        23: unknown:1DF3           {1DF3} V0    internal, hidden 
        24: unknown:1E00           {1E00} V0    hidden 
        25: unknown:1EB0           {1EB0} V0    internal, hidden 
        26: unknown:1803           {1803} V0    internal, hidden 
        27: unknown:1861           {1861} V0    internal, hidden 
        28: unknown:9001           {9001} V0    internal, hidden 
        29: unknown:9200           {9200} V0    internal, hidden 
        30: unknown:9202           {9202} V0    internal, hidden 
     Has 8 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, pos:0, group:1, group mask:g1
             reporting: default
         2: Middle Button             , default: Mouse Middle Button         => Mouse Middle Button       
             mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3
             reporting: default
         3: Back Button               , default: Mouse Back Button           => Mouse Back Button         
             mse, reprogrammable, 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, raw XY, pos:0, group:2, group mask:g1,g2
             reporting: default
         5: Mouse Gesture Button      , default: Gesture Button Navigation   => Gesture Button Navigation 
             mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3
             reporting: default
         6: Smart Shift               , default: Smart Shift                 => Smart Shift               
             mse, reprogrammable, divertable, raw XY, pos:0, group:3, group mask:g1,g2,g3
             reporting: default
         7: Virtual Gesture Button    , default: Virtual Gesture Button      => Virtual Gesture Button    
             divertable, virtual, raw XY, force raw XY, pos:0, group:4, group mask:empty
             reporting: default
     Has 2 gesture(s), 0 param(s) and 4 spec(s):
        Thumbwheel                 Enabled(   0): True   Diverted:(   0) False
        NaturalScrolling           Enabled(   1): True   Diverted:(None) None
        period unit                Spec   (   3): 800
        DVI field width            Spec   (   1): 8
        field widths               Spec   (   2): 8
        resolution                 Spec   (   4): 2048
     Battery: 20%, discharging, next level 5%.
```
</details>

- Contents of `~/.config/solaar/config.yaml`:

<details>

```
- 1.1.5
- _NAME: Wireless Mouse MX Master
  _absent: [hi-res-scroll, lowres-smooth-scroll, thumb-scroll-mode, thumb-scroll-invert, onboard_profiles, report_rate, pointer_speed, speed-change, backlight,
    backlight-timed, fn-swap, persistent-remappable-keys, disable-keyboard-keys, divert-crown, crown-smooth, divert-gkeys, m-key-leds, mr-key-led, multiplatform,
    gesture2-params, lowres-scroll-mode, sidetone, equalizer]
  _battery: 4096
  _modelId: B01E40710000
  _sensitive: {divert-keys: false, dpi: true, hires-smooth-resolution: false, smart-shift: true}
  _serial: 13CDB00E
  _unitId: 5D96335E
  _wpid: '4071'
  change-host: null
  divert-keys: {82: 0, 83: 0, 86: 0, 195: 0, 196: 0}
  dpi: 1400
  dpi-sliding: 0
  gesture2-divert: {46: false}
  gesture2-gestures: {45: true, 46: true}
  hires-scroll-mode: false
  hires-smooth-invert: false
  hires-smooth-resolution: true
  mouse-gestures: 0
  reprogrammable-keys: {80: 80, 81: 81, 82: 82, 83: 83, 86: 86, 195: 195, 196: 196}
  smart-shift: 20
- _NAME: ERGO M575
  _absent: [hi-res-scroll, lowres-smooth-scroll, hires-smooth-invert, hires-smooth-resolution, smart-shift, thumb-scroll-mode, thumb-scroll-invert, onboard_profiles,
    report_rate, pointer_speed, speed-change, backlight, backlight-timed, fn-swap, persistent-remappable-keys, disable-keyboard-keys, divert-crown, crown-smooth,
    divert-gkeys, m-key-leds, mr-key-led, multiplatform, change-host, gesture2-gestures, gesture2-divert, gesture2-params, lowres-scroll-mode, hires-scroll-mode,
    sidetone, equalizer]
  _battery: 4100
  _modelId: B02740960000
  _serial: BC4AE425
  _unitId: BC4AE425
  _wpid: '4096'
  divert-keys: {82: 0, 83: 0, 86: 0}
  dpi: 400
  dpi-sliding: 0
  mouse-gestures: 0
  reprogrammable-keys: {80: 80, 81: 81, 82: 82, 83: 83, 86: 86}
```
</details>

- Output of `solaar -dd` while reproducing the bug:

<details>

```
2022-10-24 16:25:18,680,680     INFO [MainThread] root: language en_US (UTF-8), translations path /usr/share/locale
2022-10-24 16:25:18,938,938     INFO [MainThread] logitech_receiver.diversion: GDK Keymap set up
2022-10-24 16:25:18,968,968     INFO [MainThread] solaar.upower: connected to system dbus, watching for suspend/resume events
2022-10-24 16:25:19,027,027     INFO [MainThread] solaar.ui.notify: starting desktop notifications
2022-10-24 16:25:19,454,454     INFO [MainThread] solaar.listener: starting receiver listening threads
2022-10-24 16:25:19,464,464     INFO [MainThread] solaar.listener: receiver event add DeviceInfo(path='/dev/hidraw0', vendor_id='046D', product_id='C52B', serial='', release=None, manufacturer=None, product=None, interface=2, driver='logitech-djreceiver', bus_id=3, isDevice=None)
2022-10-24 16:25:19,464,464     INFO [MainThread] logitech_receiver.base: New lock 14
2022-10-24 16:25:19,474,474     INFO [ReceiverListener:hidraw0] logitech_receiver.listener: started with <UnifyingReceiver(/dev/hidraw0,14)> (14)
2022-10-24 16:25:19,475,475     INFO [ReceiverListener:hidraw0] solaar.listener: <UnifyingReceiver(/dev/hidraw0,14)>: notifications listener has started (14)
2022-10-24 16:25:19,475,475     INFO [ReceiverListener:hidraw0] logitech_receiver.base: New lock <_ThreadedHandle(/dev/hidraw0)>
2022-10-24 16:25:19,480,480     INFO [ReceiverListener:hidraw0] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,14)>: receiver notifications enabled => ('wireless', 'software present')
2022-10-24 16:25:19,486,486     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <UnifyingReceiver(/dev/hidraw0,14)>: present, No paired devices. (0) 
2022-10-24 16:25:19,506,506     INFO [ReceiverListener:hidraw0] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,14)>: found new device 1 (4096)
2022-10-24 16:25:19,507,507     INFO [ReceiverListener:hidraw0] solaar.listener: connection Notification(10,1,41,04,529640) for <Device(1,4096,ERGO M575,BC4AE425)> (mouse)
2022-10-24 16:25:19,511,511     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <UnifyingReceiver(/dev/hidraw0,14)>: present, 1 paired device. (0) 
2022-10-24 16:25:19,512,512     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(1,4096,ERGO M575,BC4AE425)>: paired offline, {'LINK ENCRYPTED': False} (0) 
2022-10-24 16:25:19,528,528     INFO [ReceiverListener:hidraw0] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,14)>: found new device 2 (4071)
2022-10-24 16:25:19,529,529     INFO [ReceiverListener:hidraw0] solaar.listener: connection Notification(10,2,41,04,327140) for <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> (mouse)
2022-10-24 16:25:19,534,534     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <UnifyingReceiver(/dev/hidraw0,14)>: present, 2 paired devices. (0) 
2022-10-24 16:25:19,595,595     INFO [ReceiverListener:hidraw0] logitech_receiver.base: New lock 18
2022-10-24 16:25:19,802,802     INFO [ReceiverListener:hidraw0] logitech_receiver.device: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: unitId 5D96335E does not match serial 13CDB00E
2022-10-24 16:25:19,835,835     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'LINK ENCRYPTED': True, 'BATTERY LEVEL': 20, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
2022-10-24 16:25:20,259,259     INFO [ReceiverListener:hidraw0] logitech_receiver.status: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> pushing device settings [<Setting([feature:toggle] MX Master:hires-smooth-invert=None)>, <Setting([feature:toggle] MX Master:hires-smooth-resolution=None)>, <Setting([feature:toggle] MX Master:hires-scroll-mode=None)>, <Setting([feature:range] MX Master:smart-shift=None)>, <Setting([feature:choice] MX Master:dpi=None)>, <Setting([feature:map choice] MX Master:reprogrammable-keys=None)>, <Setting([feature:map choice] MX Master:divert-keys=None)>, <Setting([feature:choice] MX Master:change-host=None)>, <Setting([feature:multiple toggle] MX Master:gesture2-gestures=None)>, <Setting([feature:multiple toggle] MX Master:gesture2-divert=None)>]
2022-10-24 16:25:20,682,682     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'LINK ENCRYPTED': True, 'BATTERY LEVEL': 20, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
2022-10-24 16:25:20,683,683     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: spurious BATTERY status Notification(11,2,07,10,0404C0280A0000000000000000000000)
2022-10-24 16:25:20,684,684     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: unknown WHEEL Notification(11,2,0E,20,02000000000000000000000000000000)
2022-10-24 16:25:20,685,685     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: WHEEL: res: 0 periods: 8 delta V:3072
2022-10-24 16:25:33,557,557     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: WHEEL: ratchet: 0
2022-10-24 16:25:50,146,146     INFO [ReceiverListener:hidraw0] solaar.listener: connection Notification(10,2,41,04,727140) for <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> (mouse)
2022-10-24 16:25:50,147,147     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <UnifyingReceiver(/dev/hidraw0,14)>: present, 2 paired devices. (0) 
2022-10-24 16:25:50,149,149     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired offline, {'BATTERY LEVEL': 20} (0) 
2022-10-24 16:25:50,150,150     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: DJ connection: False Notification(20,2,42,01,0000000000000000000000)
2022-10-24 16:25:50,150,150     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired offline, {'BATTERY LEVEL': 20} (0) disconnected
2022-10-24 16:25:51,123,123     INFO [MainThread] solaar.upower: received resume event
2022-10-24 16:25:51,123,123     INFO [MainThread] solaar.listener: ping all devices when resuming
2022-10-24 16:25:51,128,128     INFO [MainThread] logitech_receiver.base: New lock 17
2022-10-24 16:25:51,135,135     INFO [MainThread] solaar.listener: status_changed <Device(1,4096,ERGO M575,BC4AE425)>: paired offline, {'LINK ENCRYPTED': False} (0) 
2022-10-24 16:25:51,143,143     INFO [MainThread] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired offline, {'BATTERY LEVEL': 20} (0) 
2022-10-24 16:26:03,307,307     INFO [ReceiverListener:hidraw0] solaar.listener: connection Notification(10,2,41,04,B27140) for <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> (mouse)
2022-10-24 16:26:03,307,307     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <UnifyingReceiver(/dev/hidraw0,14)>: present, 2 paired devices. (0) 
2022-10-24 16:26:03,340,340     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'BATTERY LEVEL': 20, 'LINK ENCRYPTED': True, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
2022-10-24 16:26:03,340,340     INFO [ReceiverListener:hidraw0] logitech_receiver.status: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> pushing device settings [<Setting([feature:toggle] MX Master:hires-smooth-invert=False)>, <Setting([feature:toggle] MX Master:hires-smooth-resolution=True)>, <Setting([feature:toggle] MX Master:hires-scroll-mode=False)>, <Setting([feature:range] MX Master:smart-shift=20)>, <Setting([feature:choice] MX Master:dpi=1400)>, <Setting([feature:map choice] MX Master:reprogrammable-keys={80: 80, 81: 81, 82: 82, 83: 83, 86: 86, 195: 195, 196: 196})>, <Setting([feature:map choice] MX Master:divert-keys={82: 0, 83: 0, 86: 0, 195: 0, 196: 0})>, <Setting([feature:choice] MX Master:change-host=1:earth)>, <Setting([feature:multiple toggle] MX Master:gesture2-gestures={45: True, 46: True})>, <Setting([feature:multiple toggle] MX Master:gesture2-divert={46: False})>]
2022-10-24 16:26:03,802,802     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'BATTERY LEVEL': 20, 'LINK ENCRYPTED': True, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) 
2022-10-24 16:26:03,803,803     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: DJ connection: True Notification(20,2,42,00,0000000000000000000000)
2022-10-24 16:26:03,803,803     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'BATTERY LEVEL': 20, 'LINK ENCRYPTED': True, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) connected
2022-10-24 16:26:03,819,819     INFO [ReceiverListener:hidraw0] logitech_receiver.status: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)> pushing device settings [<Setting([feature:toggle] MX Master:hires-smooth-invert=False)>, <Setting([feature:toggle] MX Master:hires-smooth-resolution=True)>, <Setting([feature:toggle] MX Master:hires-scroll-mode=False)>, <Setting([feature:range] MX Master:smart-shift=20)>, <Setting([feature:choice] MX Master:dpi=1400)>, <Setting([feature:map choice] MX Master:reprogrammable-keys={80: 80, 81: 81, 82: 82, 83: 83, 86: 86, 195: 195, 196: 196})>, <Setting([feature:map choice] MX Master:divert-keys={82: 0, 83: 0, 86: 0, 195: 0, 196: 0})>, <Setting([feature:choice] MX Master:change-host=1:earth)>, <Setting([feature:multiple toggle] MX Master:gesture2-gestures={45: True, 46: True})>, <Setting([feature:multiple toggle] MX Master:gesture2-divert={46: False})>]
2022-10-24 16:26:04,176,176     INFO [ReceiverListener:hidraw0] solaar.listener: status_changed <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: paired online, {'BATTERY LEVEL': 20, 'LINK ENCRYPTED': True, 'BATTERY STATUS': NamedInt(0, 'discharging'), 'BATTERY NEXT LEVEL': 5, 'BATTERY VOLTAGE': None, 'BATTERY CHARGING': False, 'ERROR': None} (0) powered on
2022-10-24 16:26:04,178,178     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: spurious BATTERY status Notification(11,2,07,10,0404C0280A0000000000000000000000)
2022-10-24 16:26:04,179,179     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: unknown WHEEL Notification(11,2,0E,20,02000000000000000000000000000000)
2022-10-24 16:26:04,181,181     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: WHEEL: res: 0 periods: 8 delta V:3072
2022-10-24 16:26:04,181,181     INFO [ReceiverListener:hidraw0] logitech_receiver.notifications: <Device(2,4071,Wireless Mouse MX Master,13CDB00E)>: WHEEL: ratchet: 1
2022-10-24 16:26:15,361,361     INFO [MainThread] solaar.listener: stopping receiver listening threads [<ReceiverListener(ReceiverListener:hidraw0, started daemon 140089561249344)>]
2022-10-24 16:26:15,375,375     INFO [MainThread] solaar.configuration: saved ['1.1.5', {'_NAME': 'Wireless Mouse MX Master', '_absent': ['hi-res-scroll', 'lowres-smooth-scroll', 'thumb-scroll-mode', 'thumb-scroll-invert', 'onboard_profiles', 'report_rate', 'pointer_speed', 'speed-change', 'backlight', 'backlight-timed', 'fn-swap', 'persistent-remappable-keys', 'disable-keyboard-keys', 'divert-crown', 'crown-smooth', 'divert-gkeys', 'm-key-leds', 'mr-key-led', 'multiplatform', 'gesture2-params', 'lowres-scroll-mode', 'sidetone', 'equalizer'], '_battery': 4096, '_modelId': 'B01E40710000', '_sensitive': {'divert-keys': False, 'dpi': True, 'hires-smooth-resolution': False, 'smart-shift': True}, '_serial': '13CDB00E', '_unitId': '5D96335E', '_wpid': '4071', 'change-host': None, 'divert-keys': {82: 0, 83: 0, 86: 0, 195: 0, 196: 0}, 'dpi': 1400, 'dpi-sliding': 0, 'gesture2-divert': {46: False}, 'gesture2-gestures': {45: True, 46: True}, 'hires-scroll-mode': False, 'hires-smooth-invert': False, 'hires-smooth-resolution': True, 'mouse-gestures': 0, 'reprogrammable-keys': {80: 80, 81: 81, 82: 82, 83: 83, 86: 86, 195: 195, 196: 196}, 'smart-shift': 20}, {'_NAME': 'ERGO M575', '_absent': ['hi-res-scroll', 'lowres-smooth-scroll', 'hires-smooth-invert', 'hires-smooth-resolution', 'smart-shift', 'thumb-scroll-mode', 'thumb-scroll-invert', 'onboard_profiles', 'report_rate', 'pointer_speed', 'speed-change', 'backlight', 'backlight-timed', 'fn-swap', 'persistent-remappable-keys', 'disable-keyboard-keys', 'divert-crown', 'crown-smooth', 'divert-gkeys', 'm-key-leds', 'mr-key-led', 'multiplatform', 'change-host', 'gesture2-gestures', 'gesture2-divert', 'gesture2-params', 'lowres-scroll-mode', 'hires-scroll-mode', 'sidetone', 'equalizer'], '_battery': 4100, '_modelId': 'B02740960000', '_serial': 'BC4AE425', '_unitId': 'BC4AE425', '_wpid': '4096', 'divert-keys': {82: 0, 83: 0, 86: 0}, 'dpi': 400, 'dpi-sliding': 0, 'mouse-gestures': 0, 'reprogrammable-keys': {80: 80, 81: 81, 82: 82, 83: 83, 86: 86}}] to /home/user/.config/solaar/config.yaml
2022-10-24 16:26:15,920,920     INFO [ReceiverListener:hidraw0] solaar.listener: <UnifyingReceiver(/dev/hidraw0,14)>: notifications listener has stopped
2022-10-24 16:26:15,924,924     INFO [MainThread] solaar.ui.notify: stopping desktop notifications
```
</details>

**Describe the bug**
When resuming from sleep the most recent ratchet setting (free spin vs. ratcheted) of the mouse is not remembered. It always defaults to ratcheted.

**To Reproduce**
Steps to reproduce the behavior:

1. Disable ratchet mode with the hardware button
2. Put OS into sleep
3. Resume from sleep
4. Ratchet mode is automatically engaged on the mouse (you can hear the mechanical switch)

**Additional context**
The mouse is an MX Master. Also see [this](https://github.com/pwr-Solaar/Solaar/issues/686#issuecomment-1289104536) comment.



Discussion:
Solaar does not take into account setting the ratchet using the smart shift button.  The enhancement should not be too difficult, stay tuned.
What is happening is that Solaar is setting the ratchet to the last state it remembers.  If the last state is free-spinnning (0 on the slider) then it will be free-spinning, otherwise it will be ratcheted.
Does the mouse automatically report these kind of changes back to the receiver? Or do we need some kind of polling?
The mouse does report this change and Solaar actually reads and understands the message, but does not adjust the value for the setting.  The only difficulty in enhancing Solaar here is whether to add a new setting to allow explicit free-to-ratchet changes via the Solaar GUI or to have the smart shift button press do something to the current setting.
I think having a separate option for toggling “automatic” free-spinning would be better.

Personally, I keep the existing free-spinning “threshold” to the default value (16 I believe), but I also like to enable/disable free-spinning completely (from the mouse) depending on what I’m doing.

I think, conceptually, the existing threshold option and automatic free-spinning are two different options.

Resolving patch:
diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py
--- a/lib/logitech_receiver/notifications.py
+++ b/lib/logitech_receiver/notifications.py
@@ -27,6 +27,7 @@
 from . import diversion as _diversion
 from . import hidpp10 as _hidpp10
 from . import hidpp20 as _hidpp20
+from . import settings_templates as _st
 from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID
 from .common import strhex as _strhex
 from .i18n import _
@@ -325,15 +326,15 @@ def _process_feature_notification(device, status, n, feature):
 
     elif feature == _F.BATTERY_VOLTAGE:
         if n.address == 0x00:
-            _ignore, level, next, battery_status, voltage = _hidpp20.decipher_battery_voltage(n.data)
-            status.set_battery_info(level, next, battery_status, voltage)
+            _ignore, level, nextl, battery_status, voltage = _hidpp20.decipher_battery_voltage(n.data)
+            status.set_battery_info(level, nextl, battery_status, voltage)
         else:
             _log.warn('%s: unknown VOLTAGE %s', device, n)
 
     elif feature == _F.UNIFIED_BATTERY:
         if n.address == 0x00:
-            _ignore, level, next, battery_status, voltage = _hidpp20.decipher_battery_unified(n.data)
-            status.set_battery_info(level, next, battery_status, voltage)
+            _ignore, level, nextl, battery_status, voltage = _hidpp20.decipher_battery_unified(n.data)
+            status.set_battery_info(level, nextl, battery_status, voltage)
         else:
             _log.warn('%s: unknown UNIFIED BATTERY %s', device, n)
 
@@ -341,8 +342,8 @@ def _process_feature_notification(device, status, n, feature):
         if n.address == 0x00:
             result = _hidpp20.decipher_adc_measurement(n.data)
             if result:
-                _ignore, level, next, battery_status, voltage = result
-                status.set_battery_info(level, next, battery_status, voltage)
+                _ignore, level, nextl, battery_status, voltage = result
+                status.set_battery_info(level, nextl, battery_status, voltage)
             else:  # this feature is used to signal device becoming inactive
                 status.changed(active=False)
         else:
@@ -432,10 +433,13 @@ def _process_feature_notification(device, status, n, feature):
                 periods = flags & 0x0f
                 _log.info('%s: WHEEL: res: %d periods: %d delta V:%-3d', device, high_res, periods, delta_v)
         elif (n.address == 0x10):
+            ratchet = ord(n.data[:1]) & 0x01
             if _log.isEnabledFor(_INFO):
-                flags = ord(n.data[:1])
-                ratchet = flags & 0x01
                 _log.info('%s: WHEEL: ratchet: %d', device, ratchet)
+            from solaar.ui.config_panel import change_setting  # prevent circular import
+            setting = next((s for s in device.settings if s.name == _st.ScrollRatchet.name), None)
+            if setting:
+                change_setting(device, setting, [2 if ratchet else 1])
         else:
             if _log.isEnabledFor(_INFO):
                 _log.info('%s: unknown WHEEL %s', device, n)
diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py
--- a/lib/logitech_receiver/settings_templates.py
+++ b/lib/logitech_receiver/settings_templates.py
@@ -394,18 +394,28 @@ def read(self, device):  # no way to read, so just assume not diverted
             return b'\x00'
 
 
+class ScrollRatchet(_Setting):
+    name = 'scroll-ratchet'
+    label = _('Scroll Wheel Ratcheted')
+    description = _('Switch the mouse wheel between speed-controlled ratcheting and always freespin.')
+    feature = _F.SMART_SHIFT
+    choices_universe = _NamedInts(**{_('Freespinning'): 1, _('Ratcheted'): 2})
+    validator_class = _ChoicesV
+    validator_options = {'choices': choices_universe}
+
+
 class SmartShift(_Setting):
     name = 'smart-shift'
-    label = _('Scroll Wheel Rachet')
+    label = _('Scroll Wheel Ratchet Speed')
     description = _(
-        'Automatically switch the mouse wheel between ratchet and freespin mode.\n'
-        'The mouse wheel is always free at 0, and always ratcheted at 50'
+        'Use the mouse wheel speed to switch between ratcheted and freespinning.\n'
+        'The mouse wheel is always ratcheted at 50.'
     )
     feature = _F.SMART_SHIFT
     rw_options = {'read_fnid': 0x00, 'write_fnid': 0x10}
 
     class rw_class(_FeatureRW):
-        MIN_VALUE = 0
+        MIN_VALUE = 1
         MAX_VALUE = 50
 
         def __init__(self, feature, read_fnid, write_fnid):
@@ -424,11 +434,11 @@ def read(self, device):
         def write(self, device, data_bytes):
             threshold = _bytes2int(data_bytes)
             # Freespin at minimum
-            mode = 1 if threshold == self.MIN_VALUE else 2
+            mode = 0  # 1 if threshold <= self.MIN_VALUE else 2
             # Ratchet at maximum
-            if threshold == self.MAX_VALUE:
+            if threshold >= self.MAX_VALUE:
                 threshold = 255
-            data = _int2bytes(mode, count=1) + _int2bytes(threshold, count=1)
+            data = _int2bytes(mode, count=1) + _int2bytes(max(0, threshold), count=1)
             return super().write(device, data)
 
     min_value = rw_class.MIN_VALUE
@@ -1167,6 +1177,7 @@ def build(cls, setting_class, device):
     HiresSmoothInvert,  # working
     HiresSmoothResolution,  # working
     HiresMode,  # simple
+    ScrollRatchet,  # simple
     SmartShift,  # working
     SmartShiftEnhanced,  # simple
     ThumbInvert,  # working


Test patch: