Building an Auto-Brightness Script with ChatGPT
Ever get tired of constantly adjusting your screen brightness depending on the lighting in your room? I did too. So, I had an idea: why not automate it? But instead of spending hours researching APIs and troubleshooting errors alone, I teamed up with ChatGPT to help me build a script that would do it all for me.
What started as a simple question—“how can I change the screen brightness in windows 11 using python and winsdk?"—quickly turned into an exciting coding session with ChatGPT. Together, we tackled challenges like:
✅ Controlling my laptop’s built-in display using WMI
✅ Adjusting brightness on my external HDMI monitor via DDC/CI
✅ Debugging tricky errors, like context manager issues with monitorcontrol
✅ Ensuring the script worked for multiple monitors seamlessly
Through trial, error, and real-time problem-solving, we ended up with a solid Python script that dynamically adjusts brightness based on ambient light—all without manual tweaking!
In this post, I’ll share how ChatGPT (o3-mini-high model) helped me write, debug, and refine this script and explain the logic behind it. Let’s dive in!
For the complete code, see the Final script section below.
You:
how can I change the screen brightness in windows 11 using python and winsdk?
ChatGPT:
To change the screen brightness in Windows 11 using Python, you can make use of the winsdk
library, which allows access to Windows SDK APIs. You will need to interact with the display’s brightness through the WmiPrvSE
service using the WMI (Windows Management Instrumentation) interface.
Here’s a Python example that shows how you can adjust screen brightness using the winsdk
library and WMI:
- Install the necessary libraries:
- You need the
wmi
andpywin32
libraries.
- You need the
pip install wmi pywin32
- Use the following Python script to change the screen brightness:
import wmi
import time
# Initialize the WMI client
w = wmi.WMI(namespace='wmi')
# Access the WmiMonitorBrightnessMethods class
monitor = w.WmiMonitorBrightnessMethods()[0]
# Function to set brightness
def set_brightness(brightness_value):
# Set the brightness to the desired level (range: 0 - 100)
monitor.WmiSetBrightness(Brightness=brightness_value, Timeout=1)
# Example usage: Set brightness to 50%
set_brightness(50)
# Optional: wait for a while and set the brightness to another value
time.sleep(2)
set_brightness(80) # Set to 80% brightness
Explanation:
- The script uses the
WmiMonitorBrightnessMethods
class from thewmi
namespace to interact with the monitor settings. - The
WmiSetBrightness
method allows you to change the brightness, with theBrightness
parameter taking a value from 0 to 100. Timeout=1
specifies how quickly the brightness should be applied.
You can adjust the set_brightness()
function to set the desired brightness level dynamically in your application.
You:
how can I access an external monitor?
ChatGPT:
To access and control an external monitor in Windows 11 using Python, you need to consider a few important things:
-
Windows Management Instrumentation (WMI): This interface provides a way to access hardware properties, such as screen brightness, for the connected display devices, including external monitors.
-
Multimonitor Setup: To work specifically with an external monitor, you will need to identify the monitor you want to control among all connected monitors.
Here’s a general approach to access and control external monitors using Python:
1. Accessing External Monitors with WMI
You can use WMI to list all connected monitors and access properties such as brightness, resolution, and device information.
Python Example to List Monitors and Adjust Brightness:
First, you need to install wmi
and pywin32
:
pip install wmi pywin32
Next, the script below lists all monitors and adjusts the brightness on the external monitor (if it’s the one you want to control).
import wmi
# Initialize the WMI client
w = wmi.WMI(namespace='wmi')
# Access all connected monitors
monitors = w.WmiMonitorBrightnessMethods()
# Print information about each connected monitor
for monitor in monitors:
print(f"Monitor Name: {monitor.__class__.__name__}")
print(f"Current Brightness: {monitor.WmiGetBrightness()}")
print("")
# Function to set brightness
def set_brightness(brightness_value):
for monitor in monitors:
monitor.WmiSetBrightness(Brightness=brightness_value, Timeout=1)
# Example: Set brightness of external monitor to 50%
set_brightness(50)
Explanation:
w.WmiMonitorBrightnessMethods()
lists all monitors, including external ones.WmiGetBrightness()
retrieves the current brightness level of each monitor.WmiSetBrightness()
allows you to set the brightness for each monitor. You can loop through the list of monitors and control them individually.
2. Identifying External Monitors
To specifically target external monitors (e.g., those connected via HDMI, DisplayPort, or VGA), you will need to filter the monitors based on their properties, such as the monitor name, device ID, or the display’s physical connection type.
For example, you can look for an external monitor by checking for specific attributes or devices in the list.
for monitor in monitors:
if "HDMI" in monitor.__class__.__name__:
print("External monitor found.")
monitor.WmiSetBrightness(Brightness=70, Timeout=1)
You could extend this approach to filter based on the monitor’s connection type or other properties.
3. More Advanced Control
For more advanced control over external monitors (like resolution or display arrangement), you would need to explore additional libraries such as pygetwindow
, screeninfo
, or use low-level Windows APIs like EnumDisplayDevices
and ChangeDisplaySettingsEx
to manipulate monitor properties.
If you have specific needs like switching displays or altering resolution, I can provide more detailed examples.
You:
why w.WmiMonitorBrightnessMethods() lists only the primary monitr?
ChatGPT:
The reason w.WmiMonitorBrightnessMethods()
lists only the primary monitor is because it specifically accesses the monitor brightness properties via the WmiMonitorBrightnessMethods
class, which is designed primarily to interact with the monitor that’s considered the “primary” or the one directly associated with the system’s primary display adapter.
In Windows, the primary monitor is the one used as the main display, often handling tasks like login screens, taskbars, and the default screen for most applications. Secondary (external) monitors are usually handled separately through other display management methods in the operating system.
Why Does This Happen?
- WMI’s Monitor Brightness API: The
WmiMonitorBrightnessMethods
class is typically tied to the primary display settings, and it doesn’t natively provide access to brightness for all monitors in a multi-monitor setup. - Display Management: External monitors may not expose brightness control through the same API that manages the primary display. The external monitors might have their own built-in controls or rely on different hardware interfaces (e.g., DDC/CI for controlling brightness) that aren’t directly accessible via WMI alone.
How to Access External Monitors?
To manage brightness or other settings for external monitors, you can use one of the following approaches:
1. Check Multiple Monitor Devices Using EnumDisplayDevices
For more control, including access to external monitors, you can use low-level Windows API calls via pywin32
. The EnumDisplayDevices
function lists all attached display devices, including external monitors, regardless of whether they’re primary or secondary.
Here’s an example to get all connected monitors:
import ctypes
from win32api import EnumDisplayDevices
def list_monitors():
monitors = []
device_index = 0
while True:
device = EnumDisplayDevices(device_index)
if not device:
break
monitors.append(device.DeviceName)
device_index += 1
return monitors
# List all monitors (primary and external)
monitors = list_monitors()
print("Connected Monitors:")
for monitor in monitors:
print(monitor)
2. Using DDC/CI Protocol for External Monitor Brightness Control
For external monitors, many display devices support DDC/CI (Display Data Channel Command Interface), which allows you to control various settings like brightness, contrast, and other parameters.
You can use a Python library like ddcutil
or monitorcontrol
to control brightness for external monitors that support DDC/CI. Here’s how you could use monitorcontrol
:
pip install monitorcontrol
Example:
from monitorcontrol import MonitorControl
# Get the external monitor (if any) and control brightness
monitor = MonitorControl.get_monitors()[1] # Index 1 would typically be the secondary monitor
monitor.set_brightness(50) # Set brightness to 50%
This approach works if your external monitor supports DDC/CI, which is common for modern displays.
Conclusion
To access external monitors, especially for tasks like adjusting brightness, you typically need to:
- Use low-level API functions (like
EnumDisplayDevices
) to identify and interact with all monitors, not just the primary. - Use external libraries (e.g.,
monitorcontrol
with DDC/CI) if you need to adjust properties like brightness on external monitors.
You:
I got the following error when trying to set the luminance error ’’’ monitorcontrol.py”, line 156, in _set_vcp_feature assert self._in_ctx, “This function must be run within the context manager ’''
ChatGPT:
The error you’re seeing (assert self._in_ctx, "This function must be run within the context manager"
) indicates that the monitorcontrol
library is trying to execute a function that should be called within a context manager, but it wasn’t used in the correct context.
To resolve this issue, you need to ensure that you’re calling the brightness control function inside the context manager provided by the monitorcontrol
library.
Here’s an example of how you can correctly use the context manager to set the brightness:
Correct Usage with Context Manager:
from monitorcontrol import MonitorControl
# Use the context manager to control brightness
with MonitorControl() as mc:
# Get the external monitor (e.g., second monitor)
monitor = mc.get_monitors()[1] # Index 1 typically refers to the second monitor
monitor.set_brightness(50) # Set brightness to 50%
Key Points:
MonitorControl()
is a context manager, and it needs to be used with thewith
statement.- Inside the
with
block, you can safely modify monitor properties like brightness. - The
mc.get_monitors()
method retrieves all connected monitors, and you can use indexing ([1]
,[0]
, etc.) to choose the monitor you want to control.
This should solve the error you’re encountering. If you still face issues, ensure that:
- Your external monitor supports DDC/CI.
- The
monitorcontrol
library is correctly installed and configured.
You:
the set_brightness method is not included in the monitorcontrol library
ChatGPT:
I understand you’re encountering an issue with the monitorcontrol
library, specifically that the set_brightness
method isn’t available. This is because the monitorcontrol
library is primarily designed for macOS and doesn’t natively support Windows environments. Consequently, attempting to use it on Windows will result in errors due to compatibility issues.
Alternative Solutions for Windows:
-
Using
screen-brightness-control
Library: Thescreen-brightness-control
library is a Python package that allows you to control the screen brightness on Linux and Windows systems. Here’s how you can use it:-
Installation:
pip install screen-brightness-control
-
Usage Example:
import screen_brightness_control as sbc # Get the current brightness current_brightness = sbc.get_brightness(display=0) print(f"Current Brightness: {current_brightness}%") # Set the brightness to 50% sbc.set_brightness(display=0, value=50)
In this example,
display=0
refers to the primary monitor. If you have multiple monitors, you can adjust thedisplay
parameter accordingly.
-
-
Using Windows Management Instrumentation (WMI): For more granular control, you can use the
wmi
library to interact with Windows Management Instrumentation. Here’s how you can adjust the brightness:-
Installation:
pip install wmi
-
Usage Example:
import wmi # Initialize the WMI client w = wmi.WMI(namespace='wmi') # Access the WmiMonitorBrightnessMethods class monitor = w.WmiMonitorBrightnessMethods()[0] # Function to set brightness def set_brightness(brightness_value): # Set the brightness to the desired level (range: 0 - 100) monitor.WmiSetBrightness(Brightness=brightness_value, Timeout=1) # Example usage: Set brightness to 50% set_brightness(50)
This script sets the brightness to 50%. You can adjust the
brightness_value
parameter as needed.
-
Important Considerations:
-
Monitor Compatibility: Not all monitors support software-controlled brightness adjustments. Ensure your monitor supports DDC/CI (Display Data Channel/Command Interface) for these methods to work.
-
Permissions: Some methods may require administrative privileges. Ensure your Python script is run with the necessary permissions.
-
Testing: Before deploying any script, test it to ensure it functions as expected on your system.
By using these alternative methods, you should be able to control the screen brightness on your Windows system effectively.
You:
improve the following script to work with any number of monitors. Take care of the exceptions when doing api calls.
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
return illuminance
# Retrieve the default ambient light sensor
light_sensor = sensors.LightSensor.get_default()
if light_sensor:
while(True):
# Obtain the current reading
reading = light_sensor.get_current_reading()
# Extract the illuminance in lux
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
# Get the current brightness
try:
current_brightness = sbc.get_brightness(display=0)
print(f"Current Brightness: {current_brightness}%")
except:
print("Failed to retrieve the current brightness.")
# Set the brightness to the corresponding value
brightness = getBrightness(illuminance)
sbc.set_brightness(display=1, value=brightness)
sbc.set_brightness(display=0, value=brightness)
#set time interval to 1 second
time.sleep(10)
else:
print("No ambient light sensor found on this device.")
ChatGPT:
Below is an improved version of your script. This version will:
- Detect all connected monitors dynamically.
- Loop through each monitor to set its brightness.
- Wrap API calls in try/except blocks to gracefully handle any errors.
- Allow for a simple conversion function (which you can further adjust) to map the ambient illuminance to a brightness percentage.
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Adjust this conversion logic as needed.
For now, it simply clamps the sensor value to the 0-100 range.
"""
# For example, you might want to have a mapping such as:
# 0-100 lux -> 10% brightness, 100-500 lux -> 50% brightness, >500 lux -> 100% brightness.
# Here we simply use the illuminance value directly, clamped to 0-100.
brightness = int(illuminance)
return max(0, min(100, brightness))
def main():
# Retrieve the default ambient light sensor
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Read from the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Retrieve information about all monitors.
try:
monitors = sbc.list_monitors()
current_brightnesses = sbc.get_brightness() # Returns brightness for all monitors.
print(f"Current Brightness Levels: {current_brightnesses}")
except Exception as e:
print(f"Error retrieving monitor information: {e}")
time.sleep(10)
continue
# Determine the desired brightness based on the sensor's reading.
brightness = getBrightness(illuminance)
print(f"Setting brightness to {brightness}% for all monitors.")
# Loop through all monitors and attempt to set their brightness.
for monitor in monitors:
try:
sbc.set_brightness(value=brightness, display=monitor)
print(f"Successfully set brightness for monitor: {monitor}")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor}: {e}")
# Wait 10 seconds before updating again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation of Improvements
-
Dynamic Monitor Handling:
Instead of hardcoding monitor indices (likedisplay=0
anddisplay=1
), the script callssbc.list_monitors()
to retrieve a list of available monitors and then iterates over them. -
Robust Exception Handling:
Each critical API call (reading the sensor, retrieving monitor info, setting brightness) is wrapped in a try/except block. If an exception occurs, the error is printed, and the script waits before trying again. -
Graceful Termination:
The main loop is wrapped in a try/except to catch aKeyboardInterrupt
so that you can stop the script cleanly.
Feel free to modify the getBrightness
function if you need a more sophisticated mapping from lux values to brightness percentages.
You:
when executing the script I got the following error: ’’’ Ambient Light Illuminance: 17.777000427246094 lux Error retrieving monitor information: string indices must be integers, not ‘str’ ’’'
ChatGPT:
The error
Error retrieving monitor information: string indices must be integers, not 'str'
suggests that one of the calls (in this case, likely the call to sbc.get_brightness()
) is receiving data in a format it isn’t expecting. In our original code, we tried to retrieve the brightness for all monitors at once by calling:
current_brightness = sbc.get_brightness() # without specifying a display
Depending on the version or backend of screen_brightness_control, this call can return data (or be processed internally) in an unexpected format (such as a string or a dictionary) which then causes the package to try and index it with a string key. A common solution is to handle monitors individually rather than rely on a “global” brightness call.
Below is a modified version of your script that:
- Retrieves the monitor list and then loops over each monitor.
- Queries and sets the brightness individually for each monitor.
- Wraps each API call in its own
try/except
block so that an error for one monitor doesn’t break the entire loop.
Feel free to adjust the getBrightness()
function to map lux values to brightness in a way that fits your needs.
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert ambient light sensor reading (in lux) to a brightness percentage.
For this example, we simply clamp the lux value to the 0-100 range.
You can adjust this logic for a more refined conversion.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def main():
# Retrieve the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Read ambient light sensor data.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Determine the desired brightness based on the illuminance.
brightness_value = getBrightness(illuminance)
print(f"Desired brightness: {brightness_value}%")
# Retrieve the list of connected monitors.
try:
monitors = sbc.list_monitors()
print(f"Connected monitors: {monitors}")
except Exception as e:
print(f"Error retrieving monitor list: {e}")
time.sleep(10)
continue
# For each monitor, get the current brightness and set the new brightness.
for monitor in monitors:
try:
# Retrieve current brightness for this monitor.
current = sbc.get_brightness(display=monitor)
print(f"Monitor {monitor} current brightness: {current}")
except Exception as e:
print(f"Error retrieving brightness for monitor {monitor}: {e}")
try:
# Set the new brightness for this monitor.
sbc.set_brightness(value=brightness_value, display=monitor)
print(f"Set brightness for monitor {monitor} to {brightness_value}%")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor}: {e}")
# Wait 10 seconds before the next update.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
What Changed?
-
Individual Monitor Handling:
Instead of callingsbc.get_brightness()
without specifying a display (which may return data in an unexpected format), we now retrieve the list of monitors withsbc.list_monitors()
and then loop over each monitor individually to:- Get its current brightness via
sbc.get_brightness(display=monitor)
. - Set the new brightness via
sbc.set_brightness(value=brightness_value, display=monitor)
.
- Get its current brightness via
-
Granular Exception Handling:
Each API call (for reading the sensor, listing monitors, getting brightness, and setting brightness) is wrapped in its owntry/except
block. This ensures that an error with one monitor or one reading won’t interrupt the entire loop. -
Improved Debug Information:
The script prints out which monitor is being processed and what its current brightness is. This can help diagnose any monitor-specific issues.
This approach should resolve the error you encountered while also making the script more robust for any number of monitors.
You:
now I have the following error ’’’ Error retrieving monitor list: string indices must be integers, not ‘str’ Ambient Light Illuminance: 20.201000213623047 lux Desired brightness: 20% ’’'
ChatGPT:
The error
string indices must be integers, not 'str'
often indicates that the code is trying to index into a string (using a string key) when it expected a list or a dictionary. In this case, the error is coming from the call to sbc.list_monitors()
. Depending on your system and the version of screen_brightness_control, this function sometimes returns data in an unexpected format (or even throws an error internally) when there’s only one monitor or when the backend doesn’t behave as expected.
A couple of approaches can help resolve this:
1. Use get_monitors_info()
Instead
The function get_monitors_info()
returns a list of dictionaries (each containing detailed information about a monitor). This is generally more robust and often avoids the “string indices” error. You can then pass each monitor’s info (or one of its identifying fields) to the brightness functions.
Below is the updated script using get_monitors_info()
:
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert ambient light sensor reading (in lux) to a brightness percentage.
This example simply clamps the lux value into the 0-100 range.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def main():
# Retrieve the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Determine the desired brightness based on the illuminance.
brightness_value = getBrightness(illuminance)
print(f"Desired brightness: {brightness_value}%")
# Retrieve monitor information.
try:
monitors = sbc.get_monitors_info()
print("Connected monitors:")
for mon in monitors:
print(mon)
except Exception as e:
print(f"Error retrieving monitor information: {e}")
time.sleep(10)
continue
# Loop through each monitor and adjust its brightness.
for monitor in monitors:
try:
# Get current brightness for the monitor.
current = sbc.get_brightness(display=monitor)
print(f"Monitor {monitor.get('name', monitor)} current brightness: {current}")
except Exception as e:
print(f"Error retrieving brightness for monitor {monitor.get('name', monitor)}: {e}")
try:
# Set the new brightness.
sbc.set_brightness(value=brightness_value, display=monitor)
print(f"Set brightness for monitor {monitor.get('name', monitor)} to {brightness_value}%")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor.get('name', monitor)}: {e}")
# Wait 10 seconds before updating again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Notes on This Version:
- Using
get_monitors_info()
:
This function returns a list of dictionaries (one per monitor). You can print these dictionaries to see keys like'name'
,'serial'
, etc. When callingget_brightness
orset_brightness
, you can pass the whole dictionary as thedisplay
parameter. - Monitor Identification in Logs:
When printing monitor information in the logs, we attempt to use the monitor’s'name'
field (if it exists) for clarity.
2. Specifying a Backend/Method (Alternative Approach)
If for some reason you prefer to use list_monitors()
(or if you encounter issues with get_monitors_info()
), you might try specifying the backend method. For example:
monitors = sbc.list_monitors(method='wmi')
or
monitors = sbc.list_monitors(method='vcp')
Check the screen_brightness_control documentation for details on the available methods and which one might be best suited for your system.
Conclusion
The first approach—switching to get_monitors_info()
—is usually the easiest fix for the “string indices must be integers, not ‘str’” error, as it returns data in a predictable dictionary format. This revised script should work with any number of monitors and handle API exceptions gracefully.
Try running the updated script and let me know if you run into any further issues!
You:
now I’ve the following error ’’’ Ambient Light Illuminance: 20.201000213623047 lux Desired brightness: 20% Error retrieving monitor information: module ‘screen_brightness_control’ has no attribute ‘get_monitors_info’ ’’’
ChatGPT:
The error
module 'screen_brightness_control' has no attribute 'get_monitors_info'
indicates that your installed version of screen_brightness_control does not include the get_monitors_info()
function. In many versions, the recommended approach is to use list_monitors()
, which returns a list of monitors (often as strings or simple identifiers).
The earlier error about “string indices must be integers, not ‘str’” occurred because our code was treating the monitor entries as dictionaries (attempting to call .get('name', monitor)
), while in your version they are likely simple strings.
Below is an updated script that:
- Uses
list_monitors()
to retrieve the monitor identifiers. - Treats each monitor as a string (or simple identifier) rather than a dictionary.
- Wraps each API call in try/except blocks to handle exceptions gracefully.
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
This example simply clamps the lux value into the 0-100 range.
You can adjust the mapping logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def main():
# Retrieve the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Determine the desired brightness based on the illuminance.
brightness_value = getBrightness(illuminance)
print(f"Desired brightness: {brightness_value}%")
# Retrieve the list of connected monitors.
try:
monitors = sbc.list_monitors()
print("Connected monitors:")
for monitor in monitors:
print(monitor)
except Exception as e:
print(f"Error retrieving monitor list: {e}")
time.sleep(10)
continue
# Loop through each monitor and adjust its brightness.
for monitor in monitors:
try:
# Get the current brightness for the monitor.
current = sbc.get_brightness(display=monitor)
print(f"Monitor {monitor} current brightness: {current}")
except Exception as e:
print(f"Error retrieving brightness for monitor {monitor}: {e}")
try:
# Set the new brightness for the monitor.
sbc.set_brightness(value=brightness_value, display=monitor)
print(f"Set brightness for monitor {monitor} to {brightness_value}%")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor}: {e}")
# Wait 10 seconds before the next update.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation
-
Using
list_monitors()
:
We now retrieve the monitor list withmonitors = sbc.list_monitors()
. This function should work with your version of screen_brightness_control. -
Treating Monitors as Identifiers:
Sincelist_monitors()
likely returns a list of strings (or simple identifiers), we no longer try to access dictionary keys (likemonitor.get('name', monitor)
). Instead, we just print the monitor identifier directly. -
Robust Exception Handling:
Each API call (reading the sensor, listing monitors, getting brightness, setting brightness) is wrapped in its own try/except block. This ensures that a problem with one call doesn’t break the entire script. -
Loop & Delay:
The script continuously updates every 10 seconds. Adjust the delay as needed.
Try running this updated script. It should now work with any number of monitors using your version of screen_brightness_control. If you encounter any further issues, please let me know!
You:
now I get again the following error ’’’ Ambient Light Illuminance: 19.39299964904785 lux Desired brightness: 19% Error retrieving monitor list: string indices must be integers, not ‘str’ ’’’ it seems that we are running in circles
ChatGPT:
The error
string indices must be integers, not 'str'
typically means that somewhere in the code a string is being treated like a dictionary (or list) when it isn’t. In our case, it appears inside the call to sbc.list_monitors()
, which in your installation is returning data in a format our code isn’t expecting.
One common fix is to try specifying the backend (or “method”) explicitly. In screen_brightness_control, you can often choose between methods like "wmi"
or "vcp"
. Depending on your system (and the monitors connected), one may work better than the other.
Below is an updated script that does the following:
- Tries to retrieve the monitor list using two different methods (
"wmi"
and"vcp"
). - Uses a helper function
get_monitors()
that attempts each method in turn and returns the monitors if successful. - Treats each monitor as a simple identifier (e.g. a string or number) since that’s what your version of the library returns.
- Wraps each API call in its own
try/except
block so that if one call fails, the rest of the script can continue running.
Try the script below:
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Here we simply clamp the lux value into the 0-100 range.
Adjust the logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def get_monitors():
"""
Attempt to retrieve the list of monitors using different backends.
Some systems work better with one method over another.
"""
methods = ['wmi', 'vcp']
for method in methods:
try:
monitors = sbc.list_monitors(method=method)
print(f"Monitors retrieved using method '{method}': {monitors}")
return monitors
except Exception as e:
print(f"Error retrieving monitors with method '{method}': {e}")
return []
def main():
# Retrieve the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Get the ambient light sensor reading.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Calculate the desired brightness.
brightness_value = getBrightness(illuminance)
print(f"Desired brightness: {brightness_value}%")
# Retrieve the monitor list using our helper.
monitors = get_monitors()
if not monitors:
print("No monitors found or unable to retrieve monitor list.")
time.sleep(10)
continue
# Adjust the brightness for each monitor.
for monitor in monitors:
try:
current = sbc.get_brightness(display=monitor)
print(f"Monitor {monitor} current brightness: {current}")
except Exception as e:
print(f"Error retrieving brightness for monitor {monitor}: {e}")
try:
sbc.set_brightness(value=brightness_value, display=monitor)
print(f"Set brightness for monitor {monitor} to {brightness_value}%")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor}: {e}")
# Wait before updating again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
How This Script Helps
-
Explicit Backend Selection:
The helper functionget_monitors()
loops over the methods"wmi"
and"vcp"
. If one method raises an error (like the string indexing issue), it will try the next. This often resolves issues where one backend misbehaves on your system. -
Simpler Monitor Handling:
Since your version of screen_brightness_control appears to return simple monitor identifiers (likely strings), we treat each monitor as such and pass it directly to the brightness functions. -
Granular Exception Handling:
Each critical section (reading sensor data, listing monitors, getting brightness, setting brightness) is wrapped in atry/except
block so that errors with one part won’t stop the whole script.
Give this version a try. If the issue persists, please let me know the exact version of screen_brightness_control you’re using (you can check with pip show screen_brightness_control
), as there might be version-specific quirks that we can work around.
You:
the problem is due to a wireless display attached to the laptop with the superscreen app, exlcude this monitor from the script logic
ChatGPT:
Below is an updated version of the script that excludes any monitor whose identifier (or name) suggests it is the wireless display used by Superscreen. In this example, the script will skip any monitor whose name contains the keywords "superscreen"
or "wireless"
. (You can adjust these keywords if needed.)
import winsdk.windows.devices.sensors as sensors
import screen_brightness_control as sbc
import time
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
For this example, we simply clamp the lux value into the 0-100 range.
Adjust the logic if you need a more refined mapping.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def get_monitors():
"""
Attempt to retrieve the list of monitors using different backends.
Some systems work better with one method over another.
"""
methods = ['wmi', 'vcp']
for method in methods:
try:
monitors = sbc.list_monitors(method=method)
print(f"Monitors retrieved using method '{method}': {monitors}")
return monitors
except Exception as e:
print(f"Error retrieving monitors with method '{method}': {e}")
return []
def is_excluded_monitor(monitor):
"""
Determine whether the monitor should be excluded from brightness control.
We exclude monitors whose identifier (or name) contains keywords like
'superscreen' or 'wireless' (case-insensitive).
"""
# If monitor is a dictionary, try to retrieve its 'name' key.
if isinstance(monitor, dict):
name = monitor.get("name", "")
else:
name = str(monitor)
name_lower = name.lower()
if "superscreen" in name_lower or "wireless" in name_lower:
return True
return False
def main():
# Retrieve the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
while True:
# Read ambient light sensor data.
try:
reading = light_sensor.get_current_reading()
illuminance = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {illuminance} lux")
except Exception as e:
print(f"Error reading ambient light sensor: {e}")
time.sleep(10)
continue
# Determine the desired brightness based on the illuminance.
brightness_value = getBrightness(illuminance)
print(f"Desired brightness: {brightness_value}%")
# Retrieve the monitor list.
monitors = get_monitors()
if not monitors:
print("No monitors found or unable to retrieve monitor list.")
time.sleep(10)
continue
# Loop through each monitor and adjust its brightness, excluding wireless displays.
for monitor in monitors:
# Exclude monitors matching the wireless display criteria.
if is_excluded_monitor(monitor):
print(f"Skipping monitor {monitor} (excluded as wireless display).")
continue
try:
current = sbc.get_brightness(display=monitor)
print(f"Monitor {monitor} current brightness: {current}")
except Exception as e:
print(f"Error retrieving brightness for monitor {monitor}: {e}")
try:
sbc.set_brightness(value=brightness_value, display=monitor)
print(f"Set brightness for monitor {monitor} to {brightness_value}%")
except Exception as e:
print(f"Failed to set brightness for monitor {monitor}: {e}")
# Wait 10 seconds before updating again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
How It Works
-
Excluding the Wireless Display:
The helper functionis_excluded_monitor(monitor)
checks whether the monitor’s name (or identifier) contains the keywords"superscreen"
or"wireless"
. If it does, that monitor is skipped in the brightness-adjustment loop. -
Dynamic Monitor Retrieval:
Theget_monitors()
function attempts to retrieve monitors using different backends ("wmi"
and"vcp"
) until one succeeds. This helps mitigate backend-related issues. -
Exception Handling:
Each critical operation (reading the sensor, listing monitors, getting or setting brightness) is wrapped intry/except
blocks so that one error doesn’t crash the entire loop.
This version should now avoid trying to control the wireless display that’s causing the error. Adjust the keywords in the is_excluded_monitor()
function if your monitor’s identifier uses a different string.
You:
rewrite the script using a library that is different from screen_brightness_control
ChatGPT:
Below is an example script that uses the built‑in Windows WMI (Windows Management Instrumentation) interface via the Python wmi
module instead of the screen_brightness_control
library. This script reads the ambient light sensor (using the same winsdk.windows.devices.sensors
module) and then adjusts the brightness of all monitors that support WMI‑based brightness control—skipping any monitor whose instance name indicates it is a wireless display (e.g. one created by the Superscreen app).
Note:
• Install the WMI module viapip install WMI
if you haven’t already.
• WMI brightness control typically works for built‑in (integrated) displays; many external monitors do not support brightness changes via WMI.
import time
import wmi
import winsdk.windows.devices.sensors as sensors
def getBrightness(illuminance):
"""
Convert ambient light sensor reading (in lux) to a brightness percentage.
For this example we simply use the integer part of the lux value,
clamping it to the range 0–100.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def is_excluded_monitor(instance_name):
"""
Returns True if the monitor (based on its InstanceName) should be excluded.
For example, if it belongs to a wireless display (e.g. from Superscreen),
its InstanceName may contain keywords like "superscreen" or "wireless".
"""
name = instance_name.lower() if instance_name else ""
return "superscreen" in name or "wireless" in name
def main():
# Get the default ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object in the 'wmi' namespace.
wmi_obj = wmi.WMI(namespace="wmi")
# Retrieve monitors that support brightness control.
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
if not monitors:
print("No monitors found that support brightness control via WMI.")
return
except Exception as e:
print("Error accessing WMI monitors:", e)
return
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Compute the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Loop through each monitor returned by WMI.
for monitor in monitors:
# Retrieve the instance name (used here to filter out wireless displays)
try:
instance_name = monitor.InstanceName
except Exception:
instance_name = "Unknown"
if is_excluded_monitor(instance_name):
print(f"Skipping monitor {instance_name} (excluded as wireless display).")
continue
# Attempt to set the brightness.
try:
# WmiSetBrightness(timeout, brightness) – timeout is in seconds.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set brightness for monitor {instance_name} to {brightness_value}%.")
except Exception as e:
print(f"Failed to set brightness for monitor {instance_name}: {e}")
# Wait before next update.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation
-
Ambient Light Sensor Reading:
The script useswinsdk.windows.devices.sensors
to get readings from the ambient light sensor. -
Brightness Mapping:
The functiongetBrightness
converts the sensor’s lux reading into a brightness percentage (here, simply clamping the integer value to 0–100). -
WMI Monitor Control:
The WMI interface is accessed in the"wmi"
namespace. The querywmi_obj.WmiMonitorBrightnessMethods()
returns a list of monitors that support brightness control. For each monitor, the methodWmiSetBrightness(timeout, brightness)
is called (with a timeout of 0 seconds) to set the brightness. -
Excluding the Wireless Display:
The functionis_excluded_monitor
checks the monitor’sInstanceName
for keywords like"superscreen"
or"wireless"
and skips those monitors. -
Robustness:
Exception handling is added around sensor reading, WMI access, and brightness setting so that one error does not crash the entire loop.
This script should adjust the brightness of all applicable monitors while ignoring the wireless display. Adjust the mapping logic in getBrightness
or the exclusion criteria in is_excluded_monitor
as needed for your setup.
You:
this script works only for the laptop display, extend it to work also for the monitor connected using the hdmi cable
ChatGPT:
Below is a revised script that uses two methods to set the brightness:
- WMI (via the Python
wmi
module) for monitors that support Windows’ built‑in brightness control (typically the integrated display). - DDC/CI (via the Python
monitorcontrol
library) for external monitors (such as those connected via HDMI) that support DDC/CI.
Both methods skip monitors that match certain keywords (like "superscreen"
or "wireless"
) so that a wireless display (such as one attached via the Superscreen app) is excluded.
Requirements:
• Install the required packages (if you haven’t already):
pip install WMI
pip install monitorcontrol
• Some external monitors must have DDC/CI enabled in their settings for the DDC/CI method to work.
• On some systems, you may need to run the script with administrator privileges.
import time
import wmi
import winsdk.windows.devices.sensors as sensors
from monitorcontrol import get_monitors
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
This simple conversion uses the integer part of the lux value, clamped to 0–100.
Adjust this mapping as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def is_excluded_monitor(identifier):
"""
Returns True if the monitor should be excluded from brightness control.
We skip any monitor whose identifier (or name) contains keywords like
'superscreen' or 'wireless' (case-insensitive).
"""
name = str(identifier).lower() if identifier else ""
return "superscreen" in name or "wireless" in name
def update_wmi_monitors(brightness_value, wmi_obj):
"""
Update monitors that support brightness control via WMI (typically integrated displays).
"""
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
for monitor in monitors:
# Use the InstanceName (if available) to decide whether to skip this monitor.
instance_name = getattr(monitor, 'InstanceName', 'Unknown')
if is_excluded_monitor(instance_name):
print(f"Skipping integrated monitor {instance_name} (excluded as wireless display).")
continue
try:
# The WmiSetBrightness(timeout, brightness) method sets the brightness.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set integrated monitor {instance_name} brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set integrated monitor {instance_name}: {e}")
except Exception as e:
print("Error accessing WMI monitors:", e)
def update_ddcci_monitors(brightness_value):
"""
Update external monitors via DDC/CI using the monitorcontrol library.
"""
try:
# get_monitors() returns a context manager that yields a list of Monitor objects.
with get_monitors() as monitors:
for monitor in monitors:
# If the monitor object has any identifying info, you could try to exclude it.
# Here we assume that monitors discovered via DDC/CI are external.
try:
monitor.set_luminance(brightness_value)
print(f"Set external monitor brightness to {brightness_value}%.")
except Exception as e:
print("Failed to set external monitor brightness:", e)
except Exception as e:
print("Error retrieving external monitors via monitorcontrol:", e)
def main():
# Retrieve the ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object for accessing integrated display brightness.
wmi_obj = wmi.WMI(namespace="wmi")
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Determine the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Update integrated monitors via WMI.
update_wmi_monitors(brightness_value, wmi_obj)
# Update external monitors via DDC/CI.
update_ddcci_monitors(brightness_value)
# Wait 10 seconds before updating again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
How This Script Works
-
Ambient Light Sensing:
The script uses the Windowswinsdk.windows.devices.sensors
module to read the current illuminance from the ambient light sensor. -
Brightness Mapping:
ThegetBrightness
function maps the lux reading to a brightness percentage (simply clamped between 0 and 100). -
Integrated Display Control (WMI):
Using thewmi
module, the script retrieves objects fromWmiMonitorBrightnessMethods()
and sets brightness viaWmiSetBrightness()
. Each monitor’sInstanceName
is checked against the exclusion criteria. -
External Monitor Control (DDC/CI):
Themonitorcontrol
library’sget_monitors()
is used to retrieve monitors that support DDC/CI commands. Each monitor’s brightness is then set usingset_luminance()
.
(If your external monitor also exposes an identifier and you wish to filter out any wireless display, you can extend the code to check properties of the Monitor object.) -
Loop and Exception Handling:
The script updates every 10 seconds and gracefully handles exceptions (e.g., sensor read errors or brightness control errors) so that one failure doesn’t halt the entire process.
This combined approach should adjust both the built‑in (laptop) display and an HDMI‑connected external monitor (provided the external monitor supports DDC/CI and has it enabled). Adjust the brightness-mapping logic and exclusion criteria as needed for your environment.
You:
it works only for the integrated monitor
ChatGPT:
Below is an updated script that uses two methods:
- WMI (via the Python
wmi
module) for the integrated (laptop) display. - DDC/CI (via the Python
monitorcontrol
library) for external monitors connected via HDMI (or DisplayPort), provided they support DDC/CI.
Important Notes:
-
DDC/CI Support:
Many external monitors require DDC/CI to be enabled in their on‑screen display (OSD) settings. If your HDMI‑connected monitor does not support DDC/CI (or if it is disabled), the script will not be able to change its brightness. -
Exclusion of Wireless Displays:
The script skips any monitor whose identifier (or name) contains keywords such as"superscreen"
or"wireless"
. -
Installation Requirements:
-
For WMI control, install the WMI module:
pip install WMI
-
For DDC/CI control, install monitorcontrol:
pip install monitorcontrol
-
Also, ensure you have the Windows SDK package for the ambient light sensor (as before).
-
Below is the complete script:
import time
import wmi
import winsdk.windows.devices.sensors as sensors
from monitorcontrol import get_monitors
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Here we simply use the integer part of the lux value and clamp it between 0 and 100.
Adjust the mapping logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def is_excluded_monitor(identifier):
"""
Returns True if the monitor (based on its identifier or name) should be excluded.
For example, skip monitors that include 'superscreen' or 'wireless'.
"""
name = str(identifier).lower() if identifier else ""
return "superscreen" in name or "wireless" in name
def update_wmi_monitors(brightness_value, wmi_obj):
"""
Update monitors that support brightness control via WMI (typically the integrated display).
"""
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
for monitor in monitors:
instance_name = getattr(monitor, 'InstanceName', 'Unknown')
if is_excluded_monitor(instance_name):
print(f"Skipping integrated monitor {instance_name} (excluded as wireless display).")
continue
try:
# WmiSetBrightness(timeout, brightness) – timeout is in seconds.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set integrated monitor {instance_name} brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set integrated monitor {instance_name}: {e}")
except Exception as e:
print("Error accessing WMI monitors:", e)
def update_external_monitors(brightness_value):
"""
Update external monitors via DDC/CI using the monitorcontrol library.
"""
try:
# get_monitors() returns a context manager yielding Monitor objects.
with get_monitors() as monitors:
monitors_list = list(monitors)
if not monitors_list:
print("No external monitors detected via DDC/CI. Make sure DDC/CI is enabled on your external monitor.")
else:
print(f"Detected {len(monitors_list)} external monitor(s):")
for idx, m in enumerate(monitors_list, start=1):
try:
# Attempt to retrieve a name or identifier, if available.
monitor_info = getattr(m, "name", f"Monitor_{idx}")
except Exception:
monitor_info = f"Monitor_{idx}"
print(f" {monitor_info}")
for monitor in monitors_list:
try:
# Set the brightness using DDC/CI.
monitor.set_luminance(brightness_value)
print(f"Set external monitor brightness to {brightness_value}%.")
except Exception as e:
print("Failed to set brightness for an external monitor:", e)
except Exception as e:
print("Error retrieving external monitors via monitorcontrol:", e)
def main():
# Retrieve the ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object for integrated display brightness control.
wmi_obj = wmi.WMI(namespace="wmi")
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Compute the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Update integrated monitor brightness via WMI.
update_wmi_monitors(brightness_value, wmi_obj)
# Update external monitor brightness via DDC/CI.
update_external_monitors(brightness_value)
# Wait 10 seconds before checking again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Troubleshooting External Monitor Control
-
DDC/CI Enabled:
Verify that your external monitor’s OSD has DDC/CI enabled. This setting is often found in the monitor’s system or communication settings. -
Monitor Detection:
When you run the script, it will print the list of external monitors detected by the monitorcontrol library. If no external monitors are detected, then either:- Your monitor does not support DDC/CI (or it is disabled), or
- The monitorcontrol library isn’t able to communicate with it over HDMI.
-
Permissions:
Depending on your system, you may need to run the script with administrative privileges to allow DDC/CI commands.
If after verifying the above your HDMI‑connected monitor still isn’t responding, it’s likely that it does not support DDC/CI control via HDMI or requires a different method. In that case, you may need to refer to your monitor’s documentation or consider using manufacturer‑specific software/API if available.
This script should work for the integrated display via WMI and (provided DDC/CI is supported and enabled) for the external HDMI‑connected monitor.
You:
DDC/CI is enabled in the monitor osd but the script is giving the following error ’’’ Error retrieving external monitors via monitorcontrol: ’list’ object does not support the context manager protocol ’’’
ChatGPT:
The error indicates that the current version of the monitorcontrol
library returns a list from get_monitors()
instead of a context manager. To fix the issue, simply remove the with
statement and iterate over the returned list directly.
Below is the updated script:
import time
import wmi
import winsdk.windows.devices.sensors as sensors
from monitorcontrol import get_monitors
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Here we simply use the integer part of the lux value and clamp it between 0 and 100.
Adjust this mapping logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def is_excluded_monitor(identifier):
"""
Returns True if the monitor (based on its identifier or name) should be excluded.
For example, skip monitors that include 'superscreen' or 'wireless'.
"""
name = str(identifier).lower() if identifier else ""
return "superscreen" in name or "wireless" in name
def update_wmi_monitors(brightness_value, wmi_obj):
"""
Update monitors that support brightness control via WMI (typically the integrated display).
"""
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
for monitor in monitors:
instance_name = getattr(monitor, 'InstanceName', 'Unknown')
if is_excluded_monitor(instance_name):
print(f"Skipping integrated monitor {instance_name} (excluded as wireless display).")
continue
try:
# WmiSetBrightness(timeout, brightness) – timeout is in seconds.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set integrated monitor {instance_name} brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set integrated monitor {instance_name}: {e}")
except Exception as e:
print("Error accessing WMI monitors:", e)
def update_external_monitors(brightness_value):
"""
Update external monitors via DDC/CI using the monitorcontrol library.
"""
try:
# get_monitors() now returns a list, so we can iterate directly.
monitors = get_monitors()
if not monitors:
print("No external monitors detected via DDC/CI. Make sure DDC/CI is enabled on your external monitor.")
else:
print(f"Detected {len(monitors)} external monitor(s).")
for idx, monitor in enumerate(monitors, start=1):
try:
# Optionally, you can try to retrieve a name or identifier if available.
monitor_info = getattr(monitor, "name", f"Monitor_{idx}")
except Exception:
monitor_info = f"Monitor_{idx}"
try:
monitor.set_luminance(brightness_value)
print(f"Set external monitor ({monitor_info}) brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set brightness for external monitor ({monitor_info}):", e)
except Exception as e:
print("Error retrieving external monitors via monitorcontrol:", e)
def main():
# Retrieve the ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object for integrated display brightness control.
wmi_obj = wmi.WMI(namespace="wmi")
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Compute the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Update integrated monitor brightness via WMI.
update_wmi_monitors(brightness_value, wmi_obj)
# Update external monitor brightness via DDC/CI.
update_external_monitors(brightness_value)
# Wait 10 seconds before checking again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation
-
Removal of Context Manager:
The external monitors are now retrieved withmonitors = get_monitors()
and then iterated directly, which resolves the error about the list not supporting the context manager protocol.
-
Integrated vs. External Monitor Updates:
The script still uses WMI to adjust the brightness of the integrated display and DDC/CI (viamonitorcontrol
) for external monitors. -
Exclusion Logic:
The functionis_excluded_monitor
continues to skip monitors (e.g., wireless displays) based on the identifier string.
Make sure that your external monitor has DDC/CI enabled in its OSD settings, and that the system has permission to control the monitor brightness via DDC/CI. If your monitor still does not respond, verify that it supports DDC/CI over the connection type (HDMI, DisplayPort, etc.).
You:
now I get the following error ’’’ Detected 3 external monitor(s). Failed to set brightness for external monitor (Monitor_1): This function must be run within the context manager Failed to set brightness for external monitor (Monitor_2): This function must be run within the context manager Failed to set brightness for external monitor (Monitor_3): This function must be run within the context manager ’’'
ChatGPT:
The error indicates that although you can retrieve external monitors as a list, each monitor must be used as a context manager when calling DDC/CI functions (like set_luminance()
). To fix this, you need to wrap each monitor object in its own with
block before calling set_luminance()
.
Below is the updated script that does this:
import time
import wmi
import winsdk.windows.devices.sensors as sensors
from monitorcontrol import get_monitors
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Here we simply use the integer part of the lux value and clamp it between 0 and 100.
Adjust the mapping logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def is_excluded_monitor(identifier):
"""
Returns True if the monitor (based on its identifier or name) should be excluded.
For example, skip monitors that include 'superscreen' or 'wireless'.
"""
name = str(identifier).lower() if identifier else ""
return "superscreen" in name or "wireless" in name
def update_wmi_monitors(brightness_value, wmi_obj):
"""
Update monitors that support brightness control via WMI (typically the integrated display).
"""
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
for monitor in monitors:
instance_name = getattr(monitor, 'InstanceName', 'Unknown')
if is_excluded_monitor(instance_name):
print(f"Skipping integrated monitor {instance_name} (excluded as wireless display).")
continue
try:
# WmiSetBrightness(timeout, brightness) – timeout is in seconds.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set integrated monitor {instance_name} brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set integrated monitor {instance_name}: {e}")
except Exception as e:
print("Error accessing WMI monitors:", e)
def update_external_monitors(brightness_value):
"""
Update external monitors via DDC/CI using the monitorcontrol library.
Each monitor must be used as a context manager when setting its brightness.
"""
try:
# Retrieve external monitors as a list.
monitors = get_monitors()
if not monitors:
print("No external monitors detected via DDC/CI. Make sure DDC/CI is enabled on your external monitor.")
else:
print(f"Detected {len(monitors)} external monitor(s).")
for idx, monitor in enumerate(monitors, start=1):
try:
monitor_info = getattr(monitor, "name", f"Monitor_{idx}")
except Exception:
monitor_info = f"Monitor_{idx}"
try:
# Use the monitor as a context manager.
with monitor:
monitor.set_luminance(brightness_value)
print(f"Set external monitor ({monitor_info}) brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set brightness for external monitor ({monitor_info}):", e)
except Exception as e:
print("Error retrieving external monitors via monitorcontrol:", e)
def main():
# Retrieve the ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object for integrated display brightness control.
wmi_obj = wmi.WMI(namespace="wmi")
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Compute the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Update integrated monitor brightness via WMI.
update_wmi_monitors(brightness_value, wmi_obj)
# Update external monitor brightness via DDC/CI.
update_external_monitors(brightness_value)
# Wait 10 seconds before checking again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation
- External Monitor Control with DDC/CI:
- The script retrieves external monitors with
get_monitors()
, which returns a list. - For each monitor, we open a context using
with monitor:
. This ensures that the monitor’s connection is properly managed and allowsset_luminance()
to be called without error.
- The script retrieves external monitors with
- Integrated Monitor Control via WMI:
- The WMI method remains unchanged for the integrated display.
- Exclusion Logic:
- The function
is_excluded_monitor
can be used to skip wireless displays or monitors associated with the Superscreen app.
- The function
Make sure that your external monitor has DDC/CI enabled in its OSD settings. This script should now work for both the integrated display (using WMI) and an HDMI-connected external monitor (using monitorcontrol with proper context management).
You:
Nice now the script works!!! remove the is_excluded_monitor function
Final script
ChatGPT:
Below is the updated script with the exclusion logic removed. All monitors (both integrated and external) will now be controlled regardless of their identifier.
import time
import wmi
import winsdk.windows.devices.sensors as sensors
from monitorcontrol import get_monitors
def getBrightness(illuminance):
"""
Convert the ambient light sensor reading (in lux) to a brightness percentage.
Here we simply use the integer part of the lux value and clamp it between 0 and 100.
Adjust the mapping logic as needed.
"""
brightness = int(illuminance)
return max(0, min(100, brightness))
def update_wmi_monitors(brightness_value, wmi_obj):
"""
Update monitors that support brightness control via WMI (typically the integrated display).
"""
try:
monitors = wmi_obj.WmiMonitorBrightnessMethods()
for monitor in monitors:
instance_name = getattr(monitor, 'InstanceName', 'Unknown')
try:
# WmiSetBrightness(timeout, brightness) – timeout is in seconds.
monitor.WmiSetBrightness(brightness_value, 0)
print(f"Set integrated monitor {instance_name} brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set integrated monitor {instance_name}: {e}")
except Exception as e:
print("Error accessing WMI monitors:", e)
def update_external_monitors(brightness_value):
"""
Update external monitors via DDC/CI using the monitorcontrol library.
Each monitor must be used as a context manager when setting its brightness.
"""
try:
# Retrieve external monitors as a list.
monitors = get_monitors()
if not monitors:
print("No external monitors detected via DDC/CI. Make sure DDC/CI is enabled on your external monitor.")
else:
print(f"Detected {len(monitors)} external monitor(s).")
for idx, monitor in enumerate(monitors, start=1):
try:
monitor_info = getattr(monitor, "name", f"Monitor_{idx}")
except Exception:
monitor_info = f"Monitor_{idx}"
try:
# Use the monitor as a context manager.
with monitor:
monitor.set_luminance(brightness_value)
print(f"Set external monitor ({monitor_info}) brightness to {brightness_value}%.")
except Exception as e:
print(f"Failed to set brightness for external monitor ({monitor_info}):", e)
except Exception as e:
print("Error retrieving external monitors via monitorcontrol:", e)
def main():
# Retrieve the ambient light sensor.
light_sensor = sensors.LightSensor.get_default()
if not light_sensor:
print("No ambient light sensor found on this device.")
return
# Create a WMI object for integrated display brightness control.
wmi_obj = wmi.WMI(namespace="wmi")
while True:
# Read the ambient light sensor.
try:
reading = light_sensor.get_current_reading()
lux = reading.illuminance_in_lux
print(f"Ambient Light Illuminance: {lux:.2f} lux")
except Exception as e:
print("Error reading ambient light sensor:", e)
time.sleep(10)
continue
# Compute the desired brightness.
brightness_value = getBrightness(lux)
print(f"Desired brightness: {brightness_value}%")
# Update integrated monitor brightness via WMI.
update_wmi_monitors(brightness_value, wmi_obj)
# Update external monitor brightness via DDC/CI.
update_external_monitors(brightness_value)
# Wait 10 seconds before checking again.
time.sleep(10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")
Explanation
-
Brightness Mapping:
ThegetBrightness()
function maps the ambient lux reading to a brightness percentage (clamped between 0 and 100). -
Integrated Monitor Control (WMI):
Theupdate_wmi_monitors()
function now sets brightness for every monitor returned by WMI without checking for any exclusion. -
External Monitor Control (DDC/CI):
Theupdate_external_monitors()
function retrieves external monitors via themonitorcontrol
library and uses each monitor as a context manager to set brightness. -
Loop:
The script continuously reads the ambient light sensor, calculates the desired brightness, and applies the setting to all monitors every 10 seconds.
This version should now adjust the brightness on all monitors (both integrated and external) without filtering any of them.