[TRACKING] "auto-brightness" aka ambient light sensor in Linux? Details?

Smooth Auto-brightness Script (Python)

I just found out that you can use systemd to run python scripts (via the same instructions from @Stike on May 25). So I wrote an auto-brightness script in python. This is my first python script ever! So bear with me; I got a little carried away and it may not be best coding practices, but it works flawlessly on my Framework 12th gen running Kubuntu 22.10.

/etc/pm/power.d/10_framework.automatic-brightness (python script)
#!/usr/bin/env python3
import os
import time
from math import copysign as cpsign


# Main Parameters
sensor_delay = 0.400
sensor_count = 5
brightness_adjust = +1.0

# Other Parameters for extra tweaking
min_brightness = 1
max_brightness = 96000
fps = 30
half_life_doubling_time = 600
threshold_ratio = 1.10
threshold_delta = 1000
min_change = int(1000/fps)

######################################
# Parameter Hints for Main parameters
# sensor_delay:
#  - positive float
#  - how often to take readings from sensor (in seconds)
#  - recommended range: 0.1 - 3.0
# sensor_count
#  - positive integer
#  - number of measurements to average together from ambient light sensor
#  - recommended range: 3 - 5 (use lower value for lower sensor_delay)
# brightness_adjust:
#  - float
#  - skews the screen brightness brighter (positive values) or dimmer (negative values) without affecting the min or max brightness
#  - recommended range: -2.5 - +2.5

######################################
# Parameter Hints for Other parameters
# min_brightness:
#  - integer
#  - screen brightness will not go below this value; values less than one will cause the screen to reach 0% brightness before light sensor bottoms out
#  - recommended range: -10,000 - 10,000
# max_brightness:
#  - positive integer
#  - numbers above 96,000 will cause the screen to reach 100% brightness before light sensor maxes out
#  - recommended range: 60,000 - 120,000
# fps:
#  - positive float
#  - the "smoothness" of brightness change; when a change is brightness is triggered, this is the number of steps of intermediate brightness per second
#  - recommended range: 5 - 45
# half_life_doubling_time:
#  - positive integer
#  - time (in milliseconds) to double or halve the current brightness, ie, rate of change
#  - recommended range: 200 - 5000
# step_ratio:
#  - float > 1.0
#  - rate at which the screen brightness changes (per step).  1.01 is slow, 2+ is nearly instant
#  - recommended range: 1.01 - 1.50 (can be smaller if steps value is high)
# threshold_ratio
#  - float >= 1.0
#  - minimum ratio of `target `to `current` before brightness change is triggered
#  - recommended range: 1.0 -1.2
# threshold_delta:
#  - non-negative integer
#  - minimum difference between `target `and `current` before brightness changes
#  - recommended range: 100 - 5000
# min_change:
#  - positive integer
#  - the minimum possible brightness change (per step).  (set very high for instant change)
#  - recommended range: 5 - 1000


#################################

# Function Definitions
echo = lambda str : os.system(f"echo {str}")

def getAmbientLight():
    f = open("/sys/bus/iio/devices/iio:device0/in_illuminance_raw")
    light = int(f.read())
    f.close()
    return light

def getScreenBrightness():
    f = open("/sys/class/backlight/intel_backlight/brightness")
    raw_bright = int(f.read())
    f.close()
    # return math.log(raw_bright/960, 10) * 50
    return raw_bright

def light2bright(light):
    percent = light / 3984
    adjusted_percent = percent ** brightness_power
    return round(adjusted_percent * (max_brightness - min_brightness) + min_brightness)


# initialize
brightness_power = 1 / (2**brightness_adjust)
steps = max(1, int(fps * sensor_delay))
step_ratio = 2 ** (1000/half_life_doubling_time/fps)
lights = [ getAmbientLight() ] * sensor_count
n = 0

# loop
while True:
    n = (n+1) % sensor_count
    l = getAmbientLight()
    lights[n] = l
    light_avg = sum(lights) / sensor_count
    echo(f"light={l} and light_avg={light_avg}")
    target = light2bright(light_avg)
    current_bright = getScreenBrightness()
    delta = target - current_bright
    ratio = (target / current_bright) ** cpsign(1, delta)
    echo(f"current_bright={current_bright} and target={target} so delta={delta} and ratio={ratio}")
    if abs(delta) > threshold_delta \
        or ratio  > threshold_ratio:
        for i in range(1, steps+1):
            echo(f"'    i={i}'")
            b = current_bright * step_ratio ** cpsign(i, delta)
            if abs(b - current_bright) < (i * min_change):
                b = current_bright + cpsign(i*min_change, delta)
            if (delta > 0 and b > target) or (delta < 0 and b < target):
                os.system(f"brightnessctl -q s {target}")
                echo(f"'    set brightness to {target}'")
                time.sleep((steps+1-i) * sensor_delay/steps)
                break;
            else:
                os.system(f"brightnessctl -q s {round(b)}")
                echo(f"'    set brightness to {round(b)}'")
            time.sleep(sensor_delay/steps)
    else:
        time.sleep(sensor_delay)

I made it highly tweakable for those who want more control over the sample rate, brightness curve, the rate and smoothness of transition, etc. Hope someone enjoys this!

Dependencies:

  • python3
  • brightnessctl (or replace with similar)
2 Likes

Since everyone is contributing their scripts, Iā€™d like to share mine too! Itā€™s actually part of my framework_toolbox, although you could separate out just the auto-brightness binary and run it on its own. I can write up instructions on how to do that if anyoneā€™s interested.

I plan on doing a rewrite of brightnessctl so that it stops being an external dependency, but currently you still need it.

The killer feature? If you manually adjust your brightness, it will remember it!

Currently forgets after the session, and because it saves the changes for each sensor value, the brightness curves can become kinda wobbly. I also need to ignore when screen dims due to idle.

2 Likes

I recall somewhere that the keyboard brightness controls interfere with the ALS, so itā€™s one or the other. Dunno if thatā€™s the problem here.

Currently I have the sensor enabled, and bound GUI+F7/8 to be brightness adjustment instead.

Hi Guys,
Glad to see the community is active in this matter!
I have been using the original script for more than 8 months now, it is great and I thank a lot the @Monster_user that took time and patience to make it.
I will surely try @Ryan_Martensā€™ script soon.

If I may, there is one feature that is absent from the scripts that are proposed:
The screen backlight brightness should not be set as an absolute value, is should be a value that the user can off-set using the keyboard keys.

It is a matter of practicality, if someone turn on the light behind you that is in sight with the sensor, it will take in account this light because it shines on it, where it does not always reflect the ambient light conditions.

Hence I think the final setting should be a value you can offset with the screen-brightness manual setting.
For instance, when the sensor reads a 0 value, you should be able to offset the value higher to allow for increase of screen back-light.
If memory serves me right, it was the case with Apple laptops, you were still able to change the brightness, but actually you were simply offsetting the back light.

Cheers

Yep, that is what my tool is meant to address. What I have done is added a brightness curve, sort of like a fan curve, that will save any adjustments you make per-ambient sensor reading.

I did a rewrite of the program logic a day ago, and I will give it a separate release with binaries and instructions as soon as I can get to it.

I just need to finish a function for cleaning up the curve and making sure it goes in one direction only (so that you donā€™t have a curve that goes dark-bright-dark-bright), but it is currently working otherwise. I also need to have it save your preferences so that changes persist across sessions.

2 Likes

Welp Iā€™m stumped trying to find this online, but suddenly my /sys/bus/iio/ folder is now missing. For me the only thing in there was the ambient light sensor.

I know often everything in there is a symlink. Could anyone here tell me what the sensor folders beneath it point to? Or if anyone knows the hardware device name thatā€™d help greatly in figuring out what happened.

Iā€™m unsure whatā€™s caused this, so any ideas there would be great too.

Thanks!

1 Like

iio:device0
points to
../../../devices/pci0000:00/0000:00:12.0/{33AECD58-B679-4E54-9BD9-A04D34F0C226}/001F:8087:0AC2.0004/HID-SENSOR-200041.3.auto/iio:device0

trigger0
points to
../../../devices/pci0000:00/0000:00:12.0/{33AECD58-B679-4E54-9BD9-A04D34F0C226}/001F:8087:0AC2.0004/HID-SENSOR-200041.3.auto/trigger0

Both of these are inside the /sys/bus/iio/devices/ folder

1 Like

Thanks so much!

I think what happened was I had an incomplete system update which was incompatible with my kernel, but this is a good reference for the future.

GitHub - mikhail-m1/illuminanced: Ambient Light Sensor Daemon for Linux There is this tool that tries to make use of multiple datapoints of acceptable brightness to calculate something more clever than linear mapping of measured luminance to backlight brightness.

1 Like

Putting this out there for any newcomers to this thread. Op and other participants, carry on. :slight_smile:

Everyone just visiting this: 11th gen has auto-brightness working find out of the box on Fedora 37/38 and Ubuntu 22.04, and the brightness keys also work out of the box.

12th/13th gen has auto-brightness working out of the box on Fedora 37/38 and Ubuntu 22.04, but to get the brightness keys working as expected, we would ask you to utilize one of our guides for Fedora or Ubuntu.

There is a grub parameter that you would add that disables auto-brightness, while providing support for the brightness keys.

Thanks for reading.

1 Like

@Matt_Hartley is it safe to assume that a fix is being worked on to allow use of both the light sensor as well as the brightness buttons?

For Ubuntu/Fedora/likely other distros as well, itā€™s either auto-brightness or the recommended parameter to get the the brightness keys working. On my Framework 13, I use the brightness keys.

Is this something that will be changed in the future? I donā€™t have an immediate answer for that at this time. However, the official recommendation is to simply use the brightness keys as auto-dim under Power on GNOME still works out of the box.

So itā€™s an upstream Fedora issue, got it. Thanks!

No. :slight_smile: Itā€™s something that would be addressed at the firmware level when a solution is available. Right now, the current practice of using the appropriate parameters is what we are recommending.

1 Like

I had illuminanced configured and working nicely on my Framework which runs openSUSE Tumbleweed, but after a reboot /sys/bus/iio/drivers is now empty. Iā€™m not sure what changed; any ideas? I donā€™t have hid_sensor_hub blacklisted and indeed itā€™s happily loaded:

# lsmod | grep sensor
hid_sensor_als         16384  0
hid_sensor_trigger     20480  1 hid_sensor_als
hid_sensor_iio_common    20480  2 hid_sensor_trigger,hid_sensor_als
hid_sensor_hub         32768  3 hid_sensor_trigger,hid_sensor_iio_common,hid_sensor_als
industrialio_triggered_buffer    12288  1 hid_sensor_trigger
industrialio          131072  6 industrialio_triggered_buffer,iio_mux,hid_sensor_trigger,kfifo_buf,iio_rescale,hid_sensor_als

Hmmm, I have not heard of GitHub - mikhail-m1/illuminanced: Ambient Light Sensor Daemon for Linux but this looks interesting awesome. This is with testing distros listed on our Linux page, of course.

Ideally and in every instance Iā€™ve ever used it with, auto-brightness should be a thing by default unless disabled (grub parameters or otherwise).

Weirdly after another reboot the ALS devices reappeared and it started working again. Not sure what changed. Anyway I made some suggestions to illuminanced on GitHub and the author was nice and responsive but said he doesnā€™t use or really maintain it any more, so ideally I guess someone else would take it over.

1 Like

Delighted itā€™s resolved. Yeah, itā€™s a good bet on some computers, less so for ours. Glad things are working again. :slight_smile:

This works great, thanks !
I had to set SensorToDisplayScale to 1 instead of 25, though

1 Like

Thought I would show off the new version of this script

It now can be stop, started, and kill (some what like a service), and with a easily modifiable sensor offset that can be changed while running (flags provided to the script ran separately will directly modify the offset)

Also the brightness only changes when the sensor sees a X% of change (10% by default)