Automated keyboard and numpad backlight control with ambient light sensor

framework already has a published GUI interface for controlling the backlight of the number pad and keyboard together. How can I use that to change the backlight of both the modules based on the ambient light sensor?

I have seen that the keyboard backlight of MacBooks automatically turn on in a dark or poorly lit room I would like to have that same for my framework 16.

I have zero experience with QMK and wanted to learn more about.

My OS currently is Windows 10 Pro.

1 Like

This would be very nice. I have no idea about how to access the light sensor from the OS, but if you manage to read the values it could definetly be done…

A quick search got me this from Reddit. Maybe useful!

But how would I implement it in the QMK code?

Don’t need to. Use qmk_hid, only not the GUI. A script can use qmk_hid via command line. --backlight [brightness percentage]

I am quite new to this and do not what to do. Can you direct me to a resource where I can understand what I need to do?
Thanks!

If you’re asking about the windows scripting part, I can’t help there, I don’t use windows. I don’t know what’s good and easy.

For the qmk part, the part that is controlling the keyboard backlights, that I can help with. For example, this should be the command to set the numpad backlight to 50 percent: qmk_hid --vid 32ac --pid 0014 via --backlight 50

Here are the pid “product ID” codes for FWL16 keyboards

vid : pid
32ac:0012 Framework Laptop 16 Keyboard Module - ANSI
32ac:0013 Framework Laptop 16 RGB Macropad
32ac:0014 Framework Laptop 16 Numpad Module
32ac:0018 Framework Laptop 16 Keyboard Module - ISO
32ac:0019 Framework Laptop 16 Keyboard Module - JIS

So the way you might want it to go is, the script checks the ambient light sensor, perhaps using the api that TheLPeink linked to, stores the value, checks if the value differs more than a choosen threshold from the last check, if it does, then ramp the keyboard backlights to match by sending repeated commands to change the backlights progressively.

Okay thank you! I will try to figure it out.

Which keyboards do you have?

ANSI and the numpad and I did try a PowerShell script with a lot of help from grok, but it seems that the ambient light sensor is not accessible using the API TheLPeink linked to. Here is the script that grok gave me.

# Framework Laptop 16 Keyboard Backlight Control Script
# Controls backlight for PIDs 0012 and 0014 using ambient light sensor and display state
# Requires qmk_hid tool installed and accessible

# Configuration
$QMK_HID_PATH = "qmk_hid"  # Path to qmk_hid.exe (update if not in PATH)
$BRIGHT_ENOUGH_THRESHOLD = 100  # Lux value where keys are visible without backlight
$POLL_INTERVAL = 1  # Seconds between checks
$DEVICE_PIDS = @("0012", "0014")  # Framework Laptop 16 keyboard PIDs (ANSI Keyboard, Numpad)
$VID = "32ac"  # Framework vendor ID

# Logging setup
$LogFile = "$PSScriptRoot\backlight_control.log"
function Write-Log {
    param($Message)
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    "$timestamp - $Message" | Out-File -FilePath $LogFile -Append
    Write-Host "$timestamp - $Message"
}

# Function to set backlight brightness for a device
function Set-BacklightBrightness {
    param (
        [string]$PID,
        [int]$Brightness
    )
    if ($Brightness -lt 0 -or $Brightness -gt 100) {
        Write-Log "Invalid brightness value: $Brightness"
        return
    }
    try {
        $command = "& $QMK_HID_PATH --vid $VID --pid $PID via --backlight $Brightness"
        Write-Log "Executing: $command"
        Invoke-Expression $command
    } catch {
        Write-Log "Error setting backlight for PID $PID : $_"
    }
}

# Function to get ambient light sensor value
function Get-AmbientLight {
    try {
        Add-Type -TypeDefinition @"
            using System;
            using System.Runtime.InteropServices;
            public class PowerManagement {
                [DllImport("user32.dll")]
                public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, int Flags);
                [DllImport("user32.dll")]
                public static extern bool UnregisterPowerSettingNotification(IntPtr handle);
                public static readonly Guid GUID_MONITOR_POWER_ON = new Guid("02731015-4510-4526-99e6-e5a17ebd1aea");
            }
"@
        $lightSensor = [Windows.Devices.Sensors.LightSensor]::GetDefault()
        if ($null -eq $lightSensor) {
            Write-Log "No ambient light sensor found."
            return $null
        }
        $reading = $lightSensor.GetCurrentReading()
        if ($null -eq $reading) {
            Write-Log "Failed to read ambient light sensor."
            return $null
        }
        $lux = $reading.IlluminanceInLux
        Write-Log "Ambient light: $lux lux"
        return $lux
    } catch {
        Write-Log "Error reading ambient light sensor: $_"
        return $null
    }
}

# Function to check if display is off
function Test-DisplayOff {
    try {
        $monitorOn = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorPowerOn
        $isOff = $monitorOn | Where-Object { $_.MonitorPowerOn -eq $false }
        if ($isOff) {
            Write-Log "Display is off."
            return $true
        }
        Write-Log "Display is on."
        return $false
    } catch {
        Write-Log "Error checking display state: $_"
        return $false
    }
}

# Main loop
function Start-BacklightControl {
    Write-Log "Starting backlight control script."
    while ($true) {
        try {
            # Check display state
            if (Test-DisplayOff) {
                foreach ($pid in $DEVICE_PIDS) {
                    Set-BacklightBrightness -PID $pid -Brightness 0
                }
                Start-Sleep -Seconds $POLL_INTERVAL
                continue
            }

            # Read ambient light
            $lux = Get-AmbientLight
            if ($null -eq $lux) {
                Start-Sleep -Seconds $POLL_INTERVAL
                continue
            }

            # Adjust backlight
            if ($lux -gt $BRIGHT_ENOUGH_THRESHOLD) {
                $brightness = 0  # Turn off in bright conditions
            } else {
                # Map lux to brightness (0-100%)
                $brightness = [math]::Max(0, [math]::Min(100, [int]((1 - $lux / $BRIGHT_ENOUGH_THRESHOLD) * 100)))
            }
            Write-Log "Setting brightness to $brightness%"
            foreach ($pid in $DEVICE_PIDS) {
                Set-BacklightBrightness -PID $pid -Brightness $brightness
            }
        } catch {
            Write-Log "Unexpected error: $_"
        }
        Start-Sleep -Seconds $POLL_INTERVAL
    }
}

# Start the script
try {
    Start-BacklightControl
} catch {
    Write-Log "Script terminated: $_"
}