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

Ok, finally made it work. Replaced the original AmbientLightFix_intel code with the provided by Monster_user above using vim. Thanks for that! Then ran sudo AmbientLightFix_RunME.sh and the screen brightness auto regulation has started working as a service even after reboot.

Hi there,
Here is a smooth integration to kubuntu 21.10.
I preferred to use systemd to integrate the script to the system and give it root privileges.

Create the script file:

sudo nano /etc/pm/power.d/10_framework.automatic-brightness

Paste there the code from Monster_user above.

Make it executable

sudo chmod 744 10_framework.automatic-brightness

Create the systemd service creation

sudo nano /lib/systemd/system/framework-automatic-brightness.service

Paste this in:

[Unit]
Description=Automatic brightness adjustment based on ambient sensor for Framework laptops (tested on Kubuntu 21.10)
After=systemd-user-sessions.service systemd-logind.service

[Service]
# User=spark
# Type=simple
# PIDFile=/run/my-service.pid
ExecStart=/etc/pm/power.d/10_framework.automatic-brightness start
Restart=on-failure
RestartSec=30
StartLimitInterval=350
StartLimitBurst=10

[Install]
WantedBy=multi-user.target

Enable the service

sudo systemctl enable framework-automatic-brightness.service

5 Likes

@Monster_user, do you know if there is a way to slow down the backlight transition rate?
Is there a command in the library to have a rate of change that is less abrupt?
Cheers

Hey, my previous laptop is a ThinkPad Yoga 370!

The ambient light sensor in that thinkpad and also the framework does work out of the box indeed on Gnome at least. Even back in 2017. I disabled it as quickly as I could because it’s annoying. Shadow of your head moves across the sensor? Brightness does down and up again. And not as smoothly as you might like.

peter@raamwerk ~> monitor-sensor 
    Waiting for iio-sensor-proxy to appear
+++ iio-sensor-proxy appeared
=== No accelerometer
=== Has ambient light sensor (value: 80,069000, unit: lux)
=== No proximity sensor
    Light changed: 80,016000 (lux)
    Light changed: 76,273000 (lux)
    Light changed: 80,123000 (lux)
    Light changed: 80,069000 (lux)
    Light changed: 79,807000 (lux)
    Light changed: 77,801000 (lux)
    Light changed: 80,278000 (lux)
    Light changed: 79,910000 (lux)

I assume monitor-sensor is a part of iio-sensor-proxy (which is not a gnome package and doesn’t have any gnome dependencies).

The wiki does say:

To make this devices work with GNOME you need to install iio-sensor-proxy package.

Which implies Gnome might be the only DE that supports ambient light sensors trough iio maybe? That line of text might also be outdated. If other desktops support it, then I expect that to just work if iio-sensor-proxy is installed. And even if your desktop doesn’t support it you could install it anyway so you can at least verify the sensor works with monitor-sensor.

Installing this package and rebooting has worked for me. Arch Linux, Gnome. I can put my hand over the sensor and the screen dims in steps. It would be great if it was smoother, but anything is better than nothing.

I made a bash script that does this with a smoother animation

It uses brightnessctl so you don’t have to run it with sudo

7 Likes

Thank you @Steel99xl , it works nicely.

If you - like me - are working with a ddc-enabled external monitor most of the time you will find that brightnessctl does not detect these. If you want to have the brightness of such an external monitor automatically adjusted along with the internal one, you can use another shell script daemon that I uploaded for you just now. Enjoy!

2 Likes

Thanks for this script, works brilliantly. I had been looking for a way to get this working on my Arch install. Even been able to tweak the numbers to give me the brightness curve I want!

1 Like

Just for sharing, this is the script (in js) I wrote myself for automatic keyboard backlight. Feel free to port it to shell script:

// auto kb light
const ILLUMINANCE_PATH = '/sys/bus/iio/devices/iio:device0/in_illuminance_raw';
const ILLUMINANCE_KBLIGHT_ON_VALUE = 25;
const ILLUMINANCE_KBLIGHT_OFF_VALUE = 40;
const ILLUMINANCE_STATUS = { kbLight: false };
setInterval(async () => {
  try {
    const illuminance = parseInt(fs.readFileSync(ILLUMINANCE_PATH).toString());
    const { kbLight } = ILLUMINANCE_STATUS;
    if (kbLight && illuminance > ILLUMINANCE_KBLIGHT_OFF_VALUE) {
      ILLUMINANCE_STATUS.kbLight = false;
      await exec('/usr/sbin/frmw_ectool pwmsetkblight 0');
    } else if (!kbLight && illuminance < ILLUMINANCE_KBLIGHT_ON_VALUE) {
      ILLUMINANCE_STATUS.kbLight = true;
      await exec('/usr/sbin/frmw_ectool pwmsetkblight 20');
    }
  } catch (e) { }
}, 10000);

If you’re unable to compile ectool yourself, and if you can trust me, the pre-compiled binary is available: hobby-framework-battery/frmw_ectool at master · ngxson/hobby-framework-battery · GitHub

2 Likes

@Steel99xl Sorry for pinging you here, but I have a question about the auto brightness script you made via brightnessctl. Is there a way I can make it less sensitive? Your scripts seems like the ideal solution, but it just always results in setting the brightness to a much dimmer level than my environment. Maybe iio-proxy in interfering?

1 Like

If you increase the
SensorToDisplayScale it will create a brighter screen based on the light sensor reading.

I have it currently set to levels I normally prefer for my environments

3 Likes

@Steel99xl Thanks! I adjusted that value and now its perfect for me! Just a suggestion, could you add a suggestion to your github page for this script that suggests that people adjust that value if they want a different outcome from this script?

1 Like

/sys/bus/iio doesn’t exists in Ubuntu 22.04. Can some one tell how to activate this in Ubuntu 22.04? I have module_blacklist=hid_sensor_hub in my grub config file, to enable brightness keyboard switches.

1 Like

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