Repository: pwr-Solaar/Solaar

Issue:
Rule condition for device on/off charger
**Information**
<!-- Please update to Solaar from this repository before asking for a new feature. -->
- Solaar version (`solaar --version` and `git describe --tags`): 1.1.8
- Distribution: Fedora 36
- Kernel version (ex. `uname -srmo`): Linux 5.14.10-300.fc35.x86_64 x86_64 GNU/Linux
- Output of `solaar show` for the target device (if applicable):

<details>

```
Bolt Receiver
  Device path  : /dev/hidraw3
  USB id       : 046d:C548
  Serial       : 31414541443844453342444142383338
  Has 1 paired device(s) out of a maximum of 6.
  Notifications: wireless, software present (0x000900)
  Device activity counters: 2=171

  2: MX Master 3S
     Device path  : None
     WPID         : B034
     Codename     : MX Master 3S
     Kind         : mouse
     Protocol     : HID++ 4.5
     Serial number: 599B7A65
     Model ID:      B03400000000
     Unit ID:       599B7A65
        Bootloader: BL1 69.00.B0003
          Firmware: RBM 22.00.B0003
             Other: 
     The power switch is located on the (unknown).
     Supports 36 HID++ 2.0 features:
         0: ROOT                   {0000} V0     
         1: FEATURE SET            {0001} V0     
         2: DEVICE FW VERSION      {0003} V4     
            Firmware: Bootloader BL1 69.00.B0003 B03438FCA4E6
            Firmware: Firmware RBM 22.00.B0003 B03438FCA4E6
            Firmware: Other   
            Unit ID: 599B7A65  Model ID: B03400000000  Transport IDs: {'btleid': 'B034'}
         3: DEVICE NAME            {0005} V0     
            Name: MX Master 3S
            Kind: mouse
         4: WIRELESS DEVICE STATUS {1D4B} V0     
         5: CONFIG CHANGE          {0020} V0     
         6: CRYPTO ID              {0021} V1     
         7: DEVICE FRIENDLY NAME   {0007} V0     
            Friendly Name: MX Master 3S
         8: UNIFIED BATTERY        {1004} V3     
            Battery: 15%, discharging.
         9: REPROG CONTROLS V4     {1B04} V5     
            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}
        10: CHANGE HOST            {1814} V1     
            Change Host        : 1:whistler
        11: HOSTS INFO             {1815} V2     
            Host 0 (paired): whistler
            Host 1 (unpaired): 
            Host 2 (unpaired): 
        12: XY STATS               {2250} V1     
        13: ADJUSTABLE DPI         {2201} V2     
            Sensitivity (DPI) (saved): 1000
            Sensitivity (DPI)        : 1000
        14: SMART SHIFT            {2110} V0     
            Scroll Wheel Ratcheted (saved): Ratcheted
            Scroll Wheel Ratcheted        : Ratcheted
            Scroll Wheel Ratchet Speed (saved): 2
            Scroll Wheel Ratchet Speed        : 2
        15: HIRES WHEEL            {2121} V1     
            Multiplier: 15
            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
            Scroll Wheel Diversion (saved): False
            Scroll Wheel Diversion        : False
        16: THUMB WHEEL            {2150} V0     
            Thumb Wheel Direction (saved): True
            Thumb Wheel Direction        : True
            Thumb Wheel Diversion (saved): False
            Thumb Wheel Diversion        : False
        17: WHEEL STATS            {2251} V0     
        18: DFUCONTROL             {00C3} V0     
        19: DEVICE RESET           {1802} V0    internal, hidden, unknown:000010 
        20: unknown:1803           {1803} V0    internal, hidden, unknown:000010 
        21: CONFIG DEVICE PROPS    {1806} V8    internal, hidden, unknown:000010 
        22: unknown:1816           {1816} V0    internal, hidden, unknown:000010 
        23: OOBSTATE               {1805} V0    internal, hidden 
        24: unknown:1830           {1830} V0    internal, hidden, unknown:000010 
        25: unknown:1891           {1891} V7    internal, hidden, unknown:000008 
        26: unknown:18A1           {18A1} V0    internal, hidden, unknown:000010 
        27: unknown:1E00           {1E00} V0    hidden 
        28: unknown:1E02           {1E02} V0    internal, hidden 
        29: unknown:1602           {1602} V0     
        30: unknown:1EB0           {1EB0} V0    internal, hidden, unknown:000010 
        31: unknown:1861           {1861} V1    internal, hidden, unknown:000010 
        32: unknown:9300           {9300} V0    internal, hidden, unknown:000010 
        33: unknown:9001           {9001} V0    internal, hidden, unknown:000010 
        34: unknown:1E22           {1E22} V0    internal, hidden, unknown:000010 
        35: unknown:9205           {9205} V0    internal, hidden, unknown:000010 
     Has 8 reprogrammable keys:
         0: Left Button               , default: Left Click                  => Left Click                
             mse, analytics key events, pos:0, group:1, group mask:g1
             reporting: default
         1: Right Button              , default: Right Click                 => Right Click               
             mse, 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, 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, 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, raw XY, analytics key events, 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, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         6: Smart Shift               , default: Smart Shift                 => Smart Shift               
             mse, reprogrammable, divertable, raw XY, analytics key events, pos:0, group:2, group mask:g1,g2
             reporting: default
         7: 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: 15%, discharging.

```
</details>


**Is your feature request related to a problem? Please describe.**
I frequently forget to put mouse on charger and the battery dies mid-session.

**Describe the solution you'd like**
I would like a rule condition which would detect when mouse is plugged in, which I could then use to perform actions.

**Describe alternatives you've considered**
Replacing my brain with an upgraded model.

**Additional context**
The goal is to create a rule to launch the screensaver when the mouse is plugged in. This would encourage me to use the mouse charging to launch the screensaver instead of the lock button on keyboard (I put a magnetic usb connector on mouse, so it's extremely easy to just move the mouse within range of the cable and it snaps into place). Yes this is a silly problem, but figure it can't hurt to ask, and others might benefit from it as well.



Discussion:
This is possible using a combination of several rule conditions but requires knowledge of the notifications that the device produces.  Adding a built-in test for this seems like a good idea.
Note that there might be multiple notifications while the device is charging, not just when the device is initially plugged in.


Resolving patch:
diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py
--- a/lib/logitech_receiver/diversion.py
+++ b/lib/logitech_receiver/diversion.py
@@ -364,6 +364,15 @@ def thumb_wheel_down(f, r, d, a):
         return False
 
 
+def charging(f, r, d, a):
+    if (f == _F.BATTERY_STATUS and r == 0 and 1 <= d[2] <= 4) or \
+       (f == _F.BATTERY_VOLTAGE and r == 0 and d[2] & (1 << 7)) or \
+       (f == _F.UNIFIED_BATTERY and r == 0 and 1 <= d[2] <= 3):
+        return 1
+    else:
+        return False
+
+
 TESTS = {
     'crown_right': [lambda f, r, d, a: f == _F.CROWN and r == 0 and d[1] < 128 and d[1], False],
     'crown_left': [lambda f, r, d, a: f == _F.CROWN and r == 0 and d[1] >= 128 and 256 - d[1], False],
@@ -379,6 +388,7 @@ def thumb_wheel_down(f, r, d, a):
     'lowres_wheel_down': [lambda f, r, d, a: f == _F.LOWRES_WHEEL and r == 0 and signed(d[0:1]) < 0 and signed(d[0:1]), False],
     'hires_wheel_up': [lambda f, r, d, a: f == _F.HIRES_WHEEL and r == 0 and signed(d[1:3]) > 0 and signed(d[1:3]), False],
     'hires_wheel_down': [lambda f, r, d, a: f == _F.HIRES_WHEEL and r == 0 and signed(d[1:3]) < 0 and signed(d[1:3]), False],
+    'charging': [charging, False],
     'False': [lambda f, r, d, a: False, False],
     'True': [lambda f, r, d, a: True, False],
 }


Test patch: