Repository: pwr-Solaar/Solaar

Issue:
Logitech g703 battery not detected/ unknown in GUI (feature BATTERY_VOLTAGE)
Hello - I have a Logitech g703 mouse using the lightspeed receiver. Solaar detects the mouse but the battery is not detected.

I'm running Ubuntu 19.10 - kernel - 5.3.0-18-generic

```
$ solaar -d
06:29:15,579     INFO [MainThread] root: language en_GB (UTF-8), translations path None
06:29:15,670     INFO [MainThread] solaar.upower: connected to system dbus, watching for suspend/resume events
06:29:15,740     INFO [MainThread] solaar.listener: starting receiver listening threads
06:29:15,820     INFO [MainThread] solaar.listener: receiver event add DeviceInfo(path=u'/dev/hidraw8', vendor_id=u'046d', product_id=u'c539', serial=u'', release='3904', manufacturer='Logitech', product='USB Receiver', interface=2, driver=u'logitech-djreceiver')
06:29:16,734  WARNING [MainThread] logitech_receiver.base: timeout (0.90/0.90) on device 255 request {80B2} params []
06:29:16,735     INFO [ReceiverListener:hidraw8] logitech_receiver.listener: started with <LightspeedReceiver(/dev/hidraw8,15)> (15)
06:29:16,735     INFO [ReceiverListener:hidraw8] solaar.listener: <LightspeedReceiver(/dev/hidraw8,15)>: notifications listener has started (15)
06:29:16,739     INFO [ReceiverListener:hidraw8] logitech_receiver.receiver: <LightspeedReceiver(/dev/hidraw8,15)>: receiver notifications enabled => ('wireless', 'software present')
06:29:16,742     INFO [ReceiverListener:hidraw8] solaar.listener: status_changed <LightspeedReceiver(/dev/hidraw8,15)>: present, No paired devices. (0) 
06:29:16,755     INFO [ReceiverListener:hidraw8] logitech_receiver.receiver: <LightspeedReceiver(/dev/hidraw8,15)>: found new device 1 (4070)
06:29:16,755     INFO [ReceiverListener:hidraw8] solaar.listener: Notification(1,41,0C,B27040) triggered new device <PairedDevice(1,4070,G703)> (mouse)
06:29:16,883     INFO [ReceiverListener:hidraw8] solaar.listener: status_changed <LightspeedReceiver(/dev/hidraw8,15)>: present, 1 paired device. (0) 
06:29:16,946     INFO [ReceiverListener:hidraw8] solaar.configuration: saved {u'4070:AFE674F7': {u'dpi': 1600, u'_name': u'G703 Wired/Wireless Gaming Mouse'}, u'_version': u'1.0.1'} to /home/max/.config/solaar/config.json
06:29:16,966     INFO [ReceiverListener:hidraw8] solaar.listener: status_changed <PairedDevice(1,4070,G703)>: paired online, {'LINK ENCRYPTED': True} (1) 
```
```
$ solaar show
Unifying Receiver
  Device path  : /dev/hidraw8
  USB id       : 046d:c539
  Serial       : 4CA99F7C
    Firmware   : 39.04.B0036
    Bootloader : 02.09
    Other      : AA.BE
  Has 1 paired device(s) out of a maximum of 1.
  Notifications: wireless, software present (0x000900)
  Device activity counters: (empty)

  1: G703 Wired/Wireless Gaming Mouse
     Codename     : G703
     Kind         : mouse
     Wireless PID : 4070
     Protocol     : HID++ 4.2
     Polling rate : 8 ms (125Hz)
     Serial number: AFE674F7
          Firmware: MPM 14.02.B0007
        Bootloader: BOT 64.02.B0007
             Other: 
     The power switch is located on the base.
     Supports 29 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
         3: DEVICE NAME            {0005}   
         4: unknown:1001           {1001}   
         5: unknown:1863           {1863}   internal, hidden
         6: unknown:18A1           {18A1}   internal, hidden
         7: unknown:1E00           {1E00}   hidden
         8: unknown:1E20           {1E20}   
         9: unknown:1EB0           {1EB0}   internal, hidden
        10: ADJUSTABLE DPI         {2201}   
        11: ANGLE SNAPPING         {2230}   
        12: SURFACE TUNING         {2240}   
        13: REPORT RATE            {8060}   
        14: ONBOARD PROFILES       {8100}   
        15: MOUSE BUTTON SPY       {8110}   
        16: unknown:1850           {1850}   internal, hidden
        17: unknown:00C2           {00C2}   
        18: unknown:1801           {1801}   internal, hidden
        19: unknown:1802           {1802}   internal, hidden
        20: unknown:1803           {1803}   internal, hidden
        21: unknown:1890           {1890}   internal, hidden
        22: unknown:1811           {1811}   internal, hidden
        23: unknown:8111           {8111}   
        24: COLOR LED EFECTS       {8070}   
        25: unknown:1809           {1809}   
        26: unknown:1830           {1830}   internal, hidden
        27: unknown:1805           {1805}   internal, hidden
        28: unknown:1806           {1806}   internal, hidden
     Battery status unavailable.

```

Any suggestions? Thanks very much



Discussion:
Yeah, it uses the `1001` feature. We need to add support for it.
Ah great - so at least a known issue.. Any idea on ETA?
thanks
Just an update to add a link to my post over in the ubuntu forums https://ubuntuforums.org/showthread.php?t=2429650
I actually sent the documentation for this feature in the kernel input mailing list. If someone wants to implement it go ahead, I don't have time right now.
Unfortunately I'm not a developer myself :( But thanks for notifying the kernel folk - hopefully someone will pick it up
@maxdraki looks like someone has already went to the trouble of implementing `0x1001` feature support, see here: https://www.spinics.net/lists/kernel/msg3296608.html for more. Not sure if/when it will be upstreamed into the mainline kernel though.
It has not been upstreamed yet and it's not ready.
Thanks for the update :)
I'm also waiting for kernel 5.4 on Ubuntu which I think should support this
No, this has not been upstreamed. It will not be present in Linux 5.4.
That's frustrating, but thanks for the update...
I am going to push for this. I already contacted the UPower guys to see what work is needed. After I get feedback I will make sure the kernel patch exports all the required information and push for it to be merged.
That would be amazing - thanks very much
Any word on this?
Also bumping for curiosty. 
Looks like it will be upstreamed in kernel version 5.6 (if it passes further approval), see [here](https://www.spinics.net/lists/linux-input/msg65046.html) for more. However, not sure what the progress is on the `upower` support.
Thanks for pointing out the update
That's frustrating - I'm guessing kernel 5.6 won't be released until March/ April - and probably won't hit Ubuntu until release 20.10.. :(
iirc upower support is dependent on the kernel update, no?
upower support depends on the kernel but still isn't done. On top of the kernel changes we need to add support in upower.
I sent a kernel patch yesterday fixing the battery reporting for these devices, which was broken. Hopefully, it will get merged in time for 5.6.

I also just merged 430b70711ba307f43751f6f6b8331e23690f0c6f which should show the voltage in `solaar show`.
Thanks a lot for the update
PR #632 should have added battery support for G703.  
By back-porting the [be281368f297] on my current kernel `5.4.x` in order to support the `1001` feature and using `master` branch I can see the battery voltage. 

Next step is to translate this to `%` and show it on UI.
```
$ solaar show                                                                                                                                                                master
Unifying Receiver
  Device path  : /dev/hidraw5
  USB id       : 046d:c539
  Serial       : 88D69A4C
    Firmware   : 39.04.B0036
    Bootloader : 02.09
    Other      : AA.BE
  Has 1 paired device(s) out of a maximum of 1.
  Notifications: wireless, software present (0x000900)
  Device activity counters: (empty)

  1: G703 LIGHTSPEED Wireless Gaming Mouse w/ HERO
     Codename     : G703 LS
     Kind         : mouse
     Wireless PID : 4086
     Protocol     : HID++ 4.2
     Polling rate : 8 ms (125Hz)
     Serial number: 5E9E8163
        Bootloader: BL1 05.01.B0013
          Firmware: MPM 22.02.B0015
             Other:
     The power switch is located on the base.
     Supports 29 HID++ 2.0 features:
         0: ROOT                   {0000}
         1: FEATURE SET            {0001}
         2: DEVICE FW VERSION      {0003}
         3: DEVICE NAME            {0005}
         4: WIRELESS DEVICE STATUS {1D4B}
         5: RESET                  {0020}
         6: BATTERY VOLTAGE        {1001}
         7: RGB EFFECTS            {8071}
         8: ONBOARD PROFILES       {8100}
         9: MOUSE BUTTON SPY       {8110}
        10: REPORT RATE            {8060}
        11: ADJUSTABLE DPI         {2201}
        12: DFUCONTROL SIGNED      {00C2}
        13: DEVICE RESET           {1802}   internal, hidden
        14: unknown:1803           {1803}   internal, hidden
        15: OOBSTATE               {1805}   internal, hidden
        16: CONFIG DEVICE PROPS    {1806}   internal, hidden
        17: unknown:1811           {1811}   internal, hidden
        18: unknown:1830           {1830}   internal, hidden
        19: unknown:1890           {1890}   internal, hidden
        20: unknown:1891           {1891}   internal, hidden
        21: unknown:18A1           {18A1}   internal, hidden
        22: unknown:1801           {1801}   internal, hidden
        23: unknown:18B1           {18B1}   internal, hidden
        24: unknown:1DF3           {1DF3}   internal, hidden
        25: unknown:1E00           {1E00}   hidden
        26: unknown:1EB0           {1EB0}   internal, hidden
        27: unknown:1863           {1863}   internal, hidden
        28: unknown:1E22           {1E22}   internal, hidden
     Battery: 4160mV, Discharging.
```

[be281368f297]: https://github.com/torvalds/linux/commit/be281368f29797cf4b318ad890673ce96bb9251e
That is just a Solaar change, the kernel doesn't have anything to do it that. That kernel patch exports the battery information to sysfs so that things like upower can pick it up.
> That is just a Solaar change, the kernel doesn't have anything to do it that. That kernel patch exports the battery information to sysfs so that things like upower can pick it up.

Interesting - thanks for the clarification. Reporting the mV is I think the first step. The next is to integrate the device's battery curve (for example [G703 battery curve]) and calculate the percentage, right?

[G703 battery curve]: https://github.com/libratbag/piper/issues/222#issuecomment-487557251
Is there any remaining problem for Solaar here?
Closing as likely addressed by PR #632 and no response from submitter.
Doen't work on current Arch Linux kernel (5.6.4). But I guess it's because of the version I'm using (1.0.1). Are there any plans to realease the 1.0.2 anytime soon or should I build it from git?

Please try from git.  1.0.2 should be released in the not too far distant future, but it would be nice to have confirmation that this bug has been fixed.
Built the git, `solaar show` now shows battery and state, but the UI says battery unknown.
Can you provide output from `solaar show` and a UI screenshot?
However, the ui says it's 1.0.1.

```bash
[foxie@foxie-home ~]$ solaar show
Lightspeed Receiver
  Device path  : /dev/hidraw3
  USB id       : 046d:c539
  Serial       : C4B998EF
    Firmware   : 39.04.B0036
    Bootloader : 02.09
    Other      : AA.BE
  Has 1 paired device(s) out of a maximum of 1.
  Notifications: wireless, software present (0x000900)
  Device activity counters: (empty)

  1: G703 LIGHTSPEED Wireless Gaming Mouse w/ HERO
     Codename     : G703 LS
     Kind         : mouse
     Wireless PID : 4086
     Protocol     : HID++ 4.2
     Polling rate : 8 ms (125Hz)
     Serial number: 280A638D
        Bootloader: BL1 05.00.B0011
          Firmware: MPM 22.00.B0011
             Other: 
     The power switch is located on the base.
     Supports 29 HID++ 2.0 features:
         0: ROOT                   {0000}   
         1: FEATURE SET            {0001}   
         2: DEVICE FW VERSION      {0003}   
         3: DEVICE NAME            {0005}   
         4: WIRELESS DEVICE STATUS {1D4B}   
         5: RESET                  {0020}   
         6: BATTERY VOLTAGE        {1001}   
         7: RGB EFFECTS            {8071}   
         8: ONBOARD PROFILES       {8100}   
         9: MOUSE BUTTON SPY       {8110}   
        10: REPORT RATE            {8060}   
        11: ADJUSTABLE DPI         {2201}   
        12: DFUCONTROL SIGNED      {00C2}   
        13: DEVICE RESET           {1802}   internal, hidden
        14: unknown:1803           {1803}   internal, hidden
        15: OOBSTATE               {1805}   internal, hidden
        16: CONFIG DEVICE PROPS    {1806}   internal, hidden
        17: unknown:1811           {1811}   internal, hidden
        18: unknown:1830           {1830}   internal, hidden
        19: unknown:1890           {1890}   internal, hidden
        20: unknown:1891           {1891}   internal, hidden
        21: unknown:18A1           {18A1}   internal, hidden
        22: unknown:1801           {1801}   internal, hidden
        23: unknown:18B1           {18B1}   internal, hidden
        24: unknown:1DF3           {1DF3}   internal, hidden
        25: unknown:1E00           {1E00}   hidden
        26: unknown:1EB0           {1EB0}   internal, hidden
        27: unknown:1863           {1863}   internal, hidden
        28: unknown:1E22           {1E22}   internal, hidden
     Battery: 3684mV, Discharging.
```
![obrázok](https://user-images.githubusercontent.com/15309350/79691173-52729880-825e-11ea-8736-d4ab0ba497b5.png)

OK.  I see that there is no code in the UI to use the voltage information.
It looks as if adding this is not trivial.  I'm going to reopen and tag this as an enhancement.


We need to use a voltage curve provided by Logitech. I have permission to use it, but someone needs to sit down and write the code.

For now I think we could start by just providing the voltage itself, and then add the battery percentage.
@thefoxie The release information mismatch in the UI is Issue #725.  There is a PR in the queue to fix it.
I don't mind working on implementing the voltage curve, but I don't posses the required data.
I did try to debug with GHub to extract some information and got this back:
![image](https://user-images.githubusercontent.com/301857/80198237-ae02a480-8617-11ea-9cdf-cefb05a0163a.png)

any plans on fixing this? I'm facing same issue with my g703
Fixing what?  The current version of Solaar reports battery voltage, which is what the device reports.  Turning that into an estimate of remaining battery capacity requires obtaining information from Logitech.
@FFY00 what's the status of obtaining this information?
Sorry to post on an old thread, but I'm not quite sure _where_ I should post this on the various projects involved.
I have a logitech g703 and I would like to get the battery _percentage_ and the _icon name_ - as normally reported by `upower`
I've recently updated to kernel 5.7.0-050700-generic
I **can** see the battery voltage in `/sys/class/power_supply/hidpp_battery_*/uevent`
>POWER_SUPPLY_VOLTAGE_NOW=4159000

But upower **does not** report this value
`solaar show` **does** report the voltage:

> Battery: 4159mV, Discharging.

But Solaar GUI **does not** 
![image](https://user-images.githubusercontent.com/3703338/88887515-6f8f0e00-d234-11ea-87ab-c013dd05fb84.png)

Any help - including where on which project I should raise this, would be gratefully received
What I would like to see is battery percentage, state and ideally the corresponding icon name - e.g. `battery-level-40-charging-symbolic`
(This is so I can create a little conky widget to keep an eye on the status and remind me to charge it before the damn thing runs out of juice!)
Thank you very much
We don't (currently) have a way of translating the voltage into an estimated percentage.
@FFY00 Do you know what the holdup in getting this information is?

That said, the voltage should be displayed in the UI even if there is no battery level information.  That's a simple enhancement.
I pinged Logitech.
@maxdraki Please try PR #878 and see whether it helps with your issue.

To run PR #878, first clone Solaar if you have not already done so and cd to the clone directory.  The first time you download the pull request, fetch it into a new branch and checkout that branch, as in:
```
git fetch origin pull/878/head:pull_878
git checkout pull_878
```
To download a new version of the pull request, pull it into your branch for the request, as in:
```
git checkout pull_878
git pull origin pull/878/head:pull_878
```

Thanks @pfps - looks like that works :+1: 
` Battery: 4107mV, discharging, average.`
![image](https://user-images.githubusercontent.com/3703338/89127843-d9cfd900-d4e8-11ea-8a69-6eeec274250c.png)
![image](https://user-images.githubusercontent.com/3703338/89127852-eb18e580-d4e8-11ea-92f6-bcb7e1f9c1c9.png)

Now if we get the voltage curve/ % that would be amazing!

Thank you very much!

Hmmm - left it running overnight and this morning the GUI seems to be picking up the wrong field
![image](https://user-images.githubusercontent.com/3703338/89148711-84c8ad00-d552-11ea-99bf-e9ea6dcc4e98.png)
CLI still OK
`Battery: 4070mV, discharging, average.`
And the voltage is back again :man_shrugging: 
![image](https://user-images.githubusercontent.com/3703338/89179833-76958380-d588-11ea-8a91-fb281e52d906.png)
Maybe because the mouse had been hibernating?

Yes, Solaar is trying to be as informative as it can be.  When your device is connected, Solaar will give the approximate level information in the form of the battery icon and the battery voltage.   When the device is not connected but was connected a short time ago (in terms of battery life), Solaar will show the approximate level information in place of the voltage (as the voltage changes relatively quickly).   When the device has been disconnected for a long period of time then Solaar will show an unknown battery level.

When your device is almost out of charge Solaar *should* pop up a notification and change the battery level to critical.  This particular behaviour has probably never been tested so it would be useful to let your battery run down sometime while Solaar is connected and see whether this happens.

Another thing that hasn't been tested is whether the battery voltage that Solaar shows changes while Solaar is running.  Can you determine whether this happens?
Hi @pfps - that makes sense, thanks.
I'll let my battery run down - probably tonight - and see if there's a notification - I'll revert here
And solaar is tracking the voltage changes - I'm tracking the battery status from `cat /sys/class/power_supply/hidpp_battery_*/uevent` - but there's nothing in `upower` for some reason. Should solaar change its icon as the voltage drops? I'm guessing not until you have the voltage curve info?
Your device should be producing HID++ 2.0 battery voltage notifications and Solaar should be responding to them so you should be seeing the voltage go lower and lower.   When your device reports that the battery is at a critical level Solaar should change the icon, but that's the only icon change that should be happening - other changes (full to nearly full to average to low to critical) need the voltage curve information.
Hi @pfps - my mouse is flashing 'red' indicating that battery is getting to critical levels.. I haven't seen the battery voltage in solaar for a while probably because I use a kvm switch with my work laptop and so switch back and forth quite a lot. It's currently showing 'average' and the icon is the normal one.
`cat /sys/class/power_supply/hidpp_battery_*/voltage_now` shows `3673000`

@pfps we can ask for the voltage when the device connects/wakes up, and then respond to the voltage notifications. This way the voltage should always be shown.
@maxdraki Can you paste output of `solaar show`?  That should show the current battery information.  But it is likely that your mouse's battery switched to a low level when the mouse was not being managed by Solaar so Solaar did not see the change-in-status notification.  I'll see whether Solaar checks battery levels when a device connects.
@FFY00 There is code in Solaar to process battery voltage notifications so that part of the solution should be working.  The problem might be that there is no code that checks battery status or voltage when devices connect.
@FFY00  Yes indeed, Solaar does not update battery status when a device reconnects unless there is no previous battery status.  It should probably update battery status whenever a device comes online.

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
@@ -259,8 +259,8 @@ def _process_feature_notification(device, status, n, feature):
 
     if feature == _F.BATTERY_VOLTAGE:
         if n.address == 0x00:
-            level, status, voltage, _ignore, _ignore = _hidpp20.decipher_voltage(n.data)
-            status.set_battery_info(level, status, None, voltage)
+            battery_level, battery_status, battery_voltage, _ignore, _ignore = _hidpp20.decipher_voltage(n.data)
+            status.set_battery_info(battery_level, battery_status, None, battery_voltage)
         else:
             _log.warn('%s: unknown VOLTAGE %s', device, n)
         return True
diff --git a/lib/logitech_receiver/status.py b/lib/logitech_receiver/status.py
--- a/lib/logitech_receiver/status.py
+++ b/lib/logitech_receiver/status.py
@@ -208,8 +208,7 @@ def set_battery_info(self, level, status, nextLevel=None, voltage=None, timestam
         old_level, self[KEYS.BATTERY_LEVEL] = self.get(KEYS.BATTERY_LEVEL), level
         old_status, self[KEYS.BATTERY_STATUS] = self.get(KEYS.BATTERY_STATUS), status
         self[KEYS.BATTERY_NEXT_LEVEL] = nextLevel
-        if voltage is not None:
-            self[KEYS.BATTERY_VOLTAGE] = voltage
+        old_voltage, self[KEYS.BATTERY_VOLTAGE] = self.get(KEYS.BATTERY_VOLTAGE), voltage
 
         charging = status in (
             _hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.full,
@@ -217,7 +216,7 @@ def set_battery_info(self, level, status, nextLevel=None, voltage=None, timestam
         )
         old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
 
-        changed = old_level != level or old_status != status or old_charging != charging
+        changed = old_level != level or old_status != status or old_charging != charging or old_voltage != voltage
         alert, reason = ALERT.NONE, None
 
         if _hidpp20.BATTERY_OK(status) and (level is None or level > _BATTERY_ATTENTION_LEVEL):
@@ -273,6 +272,7 @@ def set_battery_keys(self, battery, voltage=None):
         elif KEYS.BATTERY_STATUS in self:
             self[KEYS.BATTERY_STATUS] = None
             self[KEYS.BATTERY_CHARGING] = None
+            self[KEYS.BATTERY_VOLTAGE] = None
             self.changed()
 
     def changed(self, active=None, alert=ALERT.NONE, reason=None, timestamp=None):
@@ -293,11 +293,12 @@ def changed(self, active=None, alert=ALERT.NONE, reason=None, timestamp=None):
                         self[KEYS.NOTIFICATION_FLAGS] = d.enable_notifications()
 
                     # If we've been inactive for a long time, forget anything
-                    # about the battery.
+                    # about the battery. (This is probably unnecessary.)
                     if self.updated > 0 and timestamp - self.updated > _LONG_SLEEP:
                         self[KEYS.BATTERY_LEVEL] = None
                         self[KEYS.BATTERY_STATUS] = None
                         self[KEYS.BATTERY_CHARGING] = None
+                        self[KEYS.BATTERY_VOLTAGE] = None
 
                     # Devices lose configuration when they are turned off,
                     # make sure they're up-to-date.
@@ -306,8 +307,8 @@ def changed(self, active=None, alert=ALERT.NONE, reason=None, timestamp=None):
                     for s in d.settings:
                         s.apply()
 
-                    if self.get(KEYS.BATTERY_LEVEL) is None:
-                        self.read_battery(timestamp)
+                    # battery information may have changed so try to read it now
+                    self.read_battery(timestamp)
             else:
                 if was_active:
                     battery = self.get(KEYS.BATTERY_LEVEL)
diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py
--- a/lib/solaar/listener.py
+++ b/lib/solaar/listener.py
@@ -237,9 +237,12 @@ def _notifications_handler(self, n):
             # the receiver changed status as well
             self._status_changed(self.receiver)
 
-        assert dev
-        assert dev.status is not None
-        _notifications.process(dev, n)
+        if dev.status is None:
+            # notification before device status set up - don't process it
+            _log.warn('%s before device %s has status', n, dev)
+        else:
+            _notifications.process(dev, n)
+
         if self.receiver.status.lock_open and not already_known:
             # this should be the first notification after a device was paired
             assert n.sub_id == 0x41 and n.address == 0x04


Test patch: