Repository: nvaccess/nvda

Issue:
System tray battery message contradicts Windows and is inaccurate
<!-- Please read the text in this edit field before filling it in.
Please thoroughly read NVDA's wiki article on how to fill in this template, including how to provide the required files.
Issues may be closed if the required information is not present.
https://github.com/nvaccess/nvda/blob/master/devDocs/githubIssueTemplateExplanationAndExamples.md
Please also note that the NVDA project has a Citizen and Contributor Code of Conduct which can be found at https://github.com/nvaccess/nvda/blob/master/CODE_OF_CONDUCT.md. NV Access expects that all contributors and other community members read and abide by the rules set out in this document while participating or contributing to this project. This includes creating or commenting on issues and pull requests. 

Each of the questions and sections below start with multiple hash symbols (#). Place your answers and information on the blank line below each question.
-->

### Steps to reproduce:
Issue the NVDA announce battery status command, NVDA shift b is the desktop layout command.
### Actual behavior:
NVDA says "100 percent" if the level is 100 percent and then says "charging battery."
Use "Speak command keys" (NVDA+4) and speech viewer to copy and paste here. 
Use braille viewer to copy and paste here.
You may additionally include an explanation.
-->

### Expected behavior:
NVDA should say what it said before, "AC power on."
Use "Speak command keys" (NVDA+4) and speech viewer to copy and paste here. 
Use braille viewer to copy and paste here.
You may additionally include an explanation.
I'm not sure if this should be submitted as a bug or a feature request.  At any rate, when the battery is fully charged, charging stops.  If the charge drops below 100 percent, charging resumes until it reaches 100 percent.  The battery is thus not overcharged.  Saying charging when the battery is at one-hundred percent is factually incorrect and contradicts the information provided by Windows.  The System tray icon says "fully charged, 100 percent" in Windows 10.  A screen-reader shouldn't present contradictory information to the system.-->

### NVDA logs, crash dumps and other attachments:

### System configuration
#### NVDA installed/portable/running from source:
NVDA installed
#### NVDA version:
2022.4
#### Windows version:
Windows 10 version 22H2
#### Name and version of other software in use when reproducing the issue:

#### Other information about your system:

### Other questions
#### Does the issue still occur after restarting your computer?
Doesn't apply.
#### Have you tried any other versions of NVDA? If so, please report their behaviors.
I checked an older version I have on the system and run as a portable version.  It is version 2021.3.5.
#### If NVDA add-ons are disabled, is your problem still occurring?
Yes.
#### Does the issue still occur after you run the COM Registration Fixing Tool in NVDA's tools menu?
Doesn't apply.


Discussion:
Hi, even if Windows says fully charged, it is going through the cycle of discharging/trickle charging inside the actual battery hardware (at least that’s what some batteries will do). Also, keep in mind that Windows itself obtains battery information as reported by hardware, and this info can become inaccurate as battery ages. It might be possible to tweak NVDA to say “AC on” if battery percentage is indeed 100% as reported by Windows/battery pack hardware, but this assumes that Windows API (that’s what NVDA uses to announce battery information) is reporting accurate info from hardware. Thanks.


This contradicts #10148 which requested the oposite. I think it is still more realistic the information that it is charging, even if the batery is at 100% it gets minimal power injected. Closing as won't fix.
The question isn't what is going on inside the battery nor if Windows is providing accurate information.  the question is one that is adhered to by NVDA developers and should be here.  NVDA should not be giving information that is different than Windows.  The screen-reader is not supposed to judge the information.  Windows says fully charged.  Whether it is right or wrong isn't the point.  That's what it says.  If people don't like what it says, that should be taken up with Microsoft.  The screen-reader should not editorialize and give different information than Windows does.

NVDA used to say AC power on for a long time.  I didn't object to that.  I didn't know if sighted users saw some sort of icon or image that told them that and if NVDA was voicing the equivalent.  But this is different.  NVDA is injecting its own comment or opinion about what is occurring outside of information given by Microsoft.  

While this is a minor issue, it violates basic principles NVDA developers adhere to.  

I want the same information as Windows provides when I issue the battery level command.  If Windows says fully charged, the command should say fully charged.  If Windows says charging when not at 100 percent, that is what NVDA should say.

I don't object to NVDA giving additional information if there is a good reason but the information should not contradict information shown by Windows.  One of thirty spoken in a list is additional information but it doesn't contradict information given by Windows.

Hi, in this case, I advise replying to an earlier issue as it was closed as possibly contradicting that earlier issue (sorr). Thanks.


More than that, what Windows presents to the user is also not always accurate. So it is not always a good idea to present things the way Windows does.Von meinem iPhone gesendetAm 17.03.2023 um 02:50 schrieb Joseph Lee ***@***.***>:﻿
Hi, in this case, I advise replying to an earlier issue as it was closed as possibly contradicting that earlier issue (sorr). Thanks.



—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you modified the open/close state.Message ID: ***@***.***>
Apple's IOS Voiceover for example reports "batery charging" even when the batery is at 100% and you plug in the cable.
Looking through the code, it seems NVDA gives the ownership to the API to provide the information, we don't adjust this. So this information is exposed as such. Probably Windows tweaks the notification for its UI in a way that's different to the accessibility API.
I'll also add that, visually, even when the battery is at 100% charge the icon in the system tray presents the battery as charging.  Any time a laptop has its AC power supply attached the icon regarding the battery state shows charging.  If you hover over it the pop-up reads, "Battery status: fully charged 100%," but it is, in effect, still being constantly charged to maintain that state and that's controlled via the hardware.

No modern device can "overcharge" the battery because the whole process is regulated at the level of the hardware.  Worrying about overcharging is just not worth doing, as you have zero control over the process anyway.
If that is true, that Windows controls the message, then the question is, do you show what the system tray shows when I move to the icon or what it shows visually, evidently, when you just look at it.  Be that as it may, if I use NVDA version 2021.3.5 I get a completely different message, showing the percent and saying AC power on.  That's the message I've seen in a lot of earlier versions.  This charging message message is in quite new versions, which is why I thought it was an NVDA change.  

I don't know enough technically to know if recent versions of NVDA have changed whether NVDA controls the message or not and whether a Windows update has changed the message.

> Hi, in this case, I advise replying to an earlier issue as it was closed as possibly contradicting that earlier issue (sorr). Thanks.

This is probably my last comment in the discussion as I think I have said all I have to say.  

I thought the reference to contradicting an earlier issue was to another issue I read about power wording.  I checked the earlier issue and it actually does deal with the same subject and I may comment there.  I will quote from that issue here and the quote appears to contradict the assertion that Windows controls this wording.
This is now changed as expected in NVDA 2023.1 RC1. NVDA reports "charging", when the cable is pluged in. How ever, this occurs in Windows 11, I cannot speak for Windows 10. 

In other words, NVDA developers evidently changed this wording when NVDA shift b is used.  

It does effect Windows 10.  I am running it.  

My phone doesn't say charging when the battery is charged, My previous phone didn't either.  JAWS doesn't say charging.  It is inaccurate to say that.  Fully charged is what the system tray says when I read the information using NVDA.  I can cite, if desired, articles from reputable sites that say that when a lithium ion battery is charged, charging stops.  When the battery falls below 100 percent, charging resumes.  So when the battery is at one-hundred percent, charging has stopped.  I wouldn't object to charged being said.  I do object to charging being spoken.
I wouldn't object to the message sayind AC Power On as it did for a lot of previous versions.  The other thread begins with someone saying that AC Power On might be confusing for the average user.  I really don't think the average user is that obtuse.  If this actually is a concern, then a clearer and accurate message should be used, though I see no problem with the previous one.
I want to add that I wouldn't object if the announcement was percent followed by plugged in instead of AC Power on.  I also wouldn't object if the message said x percent charged plugged in or if the announcement said charging until 100 percent, then said charged with plugged in spoken after.  I do object to saying charging at one-hundred percent, however.  

Tis is my last message in the discussion.  I don't think I have more to say.
@Adriani90 wrote:
> Looking through the code, it seems NVDA gives the ownership to the API to provide the information, we don't adjust this. So this information is exposed as such.

Why do you say that?

We appear to be using `kernel32.GetSystemPowerStatus` for this. As I understand 
it, based on [this Microsoft reference given in winAPI/_powerTracking.py](https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-system_power_status), that returns a struct of bytes, no text at all.

One of those bytes is 0 or 1 based on whether the power is plugged in. This is 
probably why (and how) the battery icon in my system tray says "80% (plugged 
in)", which is apparently the Windows preferred terminology.

It provides another byte, the value of which can be 8 for "charging", but that is a 
different value than the AC connected or not byte.

In `globalCommands.py`, we import and use `winAPI._powerTracking.reportCurrentBatteryStatus`, when the battery status key is 
pressed.

In `winAPI/_powerTracking.py`, after some hoop jumping, we end up calling 
`winKernel.GetSystemPowerStatus()`. However, as stated, that only uses the kernel 
to get a struct of bytes containing ints, no text.

The actual text, is provided by `winAPI._powerTracking._getACStatusText()`:

```
		# Translators: Reported when the battery is plugged in, and now is charging.
		return _("Charging battery")
	else:
		# Translators: Reported when the battery is no longer plugged in, and now is not charging.
		return _("AC disconnected")
```

Conclusion: we are unquestionably providing this text, as Microsoft intended. We are given the status information--plugged or not plugged, charging or other--and are expected to use it as we see fit. We are not handing off to the API to get the text to give the user.

In addition to the reference above, here is [another](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getsystempowerstatus).
And some [sample code](https://gist.github.com/wangqr/e8cf4f4eeed891858a029eaabb0f666a) in Python to do your own battery checking.

Technically, @Gene703122 is very much correct. The value we are reporting as "charging", is actually only reporting whether AC is on or off. If that was sufficient to indicate charging, there wouldn't also be a battery status flag which includes a value which indicates "charging".

Therefore, in my opinion, if we are going to report "charging", it should be because the byte is set to charging, because that's what the OS wants us to know.

If, however, we aren't going to bother checking the byte that indicates charging (which we can, but don't), and are instead going to only read the AC connected status byte and report that to the user, then we should report what that byte actually tells us: plugged in or not.

In testing with my laptop running Win 10 22H2, NVDA reads the following values at different times, from the battery icon in the system tray (captured with [Speech Logger](https://addons.nvda-project.org/addons/speechLogger.en.html)):

* `1 hr 40 min (71%) remaining  button` (unplugged)
* `Charging battery 70 percent` (From NVDA, upon plugging in)
* `70% available (plugged in)  button` (How Windows represents it)
* `70 percent Charging battery` (what NVDA reports 30+ minutes later, when it would have definitely had charged by at least 1%, had it been charging)

### Unrelated note to anyone who may work on this module

The "Reserved1" PowerStatus byte, [now represents](https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-system_power_status#:~:text=Note%C2%A0%C2%A0This%20flag%20and%20the%20GUID_POWER_SAVING_STATUS%20GUID%20were%20introduced%20in%20Windows%C2%A010.%20This%20flag%20was%20previously%20reserved%2C%20named%20Reserved1%2C%20and%20had%20a%20value%20of%200.) the status of "Battery Saver". Even if NVDA doesn't care about indicating this yet, we should represent it correctly in the source.

There is apparently a [bug with the BYTE ctype](https://github.com/python/cpython/issues/60580), which may effect us. According to stuff I've read, we may want to switch to c_ubyte until Python 3.12, where this was fixed. I have not confirmed that however.
@XLTechie thanks for you input, I seem to have missed that part of winAPI._powerTracking._getACStatusText. I will reopen this issue and will close the old one in favor of this, because it seems Windows exposes the status "fully charged" and we should consider this availability definitely for implementation.
However, AC Power off or on is not a very user friendly message indeed. I would propose following:
* "Battery charging" when pluged in and capacity <100%
* "Fully charged; charging cable connected" when capacity = 100%, and charging cable is connected
* "Fully charged; charging cable disconnected" when capacity = 100%, and charging cable is disconnected
* "Charging cable disconnected" when capacity < 100% and the charging cable is disconnected.



Resolving patch:
diff --git a/source/winAPI/_powerTracking.py b/source/winAPI/_powerTracking.py
--- a/source/winAPI/_powerTracking.py
+++ b/source/winAPI/_powerTracking.py
@@ -216,10 +216,10 @@ def _getACStatusText(systemPowerStatus: SystemPowerStatus) -> str:
 	# Translators: This is presented to inform the user of the current battery percentage.
 	if systemPowerStatus.ACLineStatus & PowerState.AC_ONLINE:
 		# Translators: Reported when the battery is plugged in, and now is charging.
-		return _("Charging battery")
+		return _("Plugged in")
 	else:
 		# Translators: Reported when the battery is no longer plugged in, and now is not charging.
-		return _("AC disconnected")
+		return _("Unplugged")
 
 
 def _getBatteryInformation(systemPowerStatus: SystemPowerStatus) -> List[str]:


Test patch:
diff --git a/tests/unit/test_winAPI/test_powerTracking.py b/tests/unit/test_winAPI/test_powerTracking.py
--- a/tests/unit/test_winAPI/test_powerTracking.py
+++ b/tests/unit/test_winAPI/test_powerTracking.py
@@ -69,7 +69,7 @@ def test_fetch_status_full_report(self):
 			oldPowerState=PowerState.AC_OFFLINE,
 		)
 		self.assertEqual(
-			['1 percent', '1 hours and 1 minutes remaining', "AC disconnected"],
+			['1 percent', '1 hours and 1 minutes remaining', "Unplugged"],
 			actualSpeech,
 		)
 
@@ -96,7 +96,7 @@ def test_ac_status_change_statusChanged_connected(self):
 			oldPowerState=PowerState.AC_OFFLINE,
 		)
 		self.assertEqual(
-			["Charging battery", '1 percent', '1 hours and 1 minutes remaining'],
+			["Plugged in", '1 percent', '1 hours and 1 minutes remaining'],
 			actualSpeech,
 		)
 
@@ -110,7 +110,7 @@ def test_ac_status_change_statusChanged_disconnected(self):
 			oldPowerState=PowerState.AC_ONLINE,
 		)
 		self.assertEqual(
-			["AC disconnected", '1 percent', '1 hours and 1 minutes remaining'],
+			["Unplugged", '1 percent', '1 hours and 1 minutes remaining'],
 			actualSpeech,
 		)
 
@@ -124,7 +124,7 @@ def test_ac_status_change_batteryLifetimeUnknown(self):
 			oldPowerState=PowerState.AC_ONLINE,
 		)
 		self.assertEqual(
-			["AC disconnected", '1 percent'],
+			["Unplugged", '1 percent'],
 			actualSpeech,
 		)
 
@@ -139,6 +139,6 @@ def test_ac_status_change_batteryLifePercent(self):
 			oldPowerState=PowerState.AC_ONLINE,
 		)
 		self.assertEqual(
-			["AC disconnected", '7 percent'],
+			["Unplugged", '7 percent'],
 			actualSpeech,
 		)