/ambient-walrus

The walrus adjusts your lighting to suit the mood

Primary LanguageRustMozilla Public License 2.0MPL-2.0

The Ambient Walrus

This is a simple service for monitoring ambient light sensors and adjusting the brightness of things. This makes it easier to use a laptop or other portable device in changing lighting conditions.

The Walrus monitors some number of sensors (currently 1) and applies adjustment to any number of controls.

Quickstart

cargo run generate --overwrite  # NOTE: will overwrite an existing config!
cargo run

This will drive your backlight until you stop it. To find out more about command line options and such, run:

cargo run help

If you like the results, install the program using cargo install --path . --locked.

Sensor Support

Currently supports a single input sensor, chosen from the following types:

  • iio-sensors-proxy: read Industrial I/O light sensors through the iio-sensors-proxy service (installed separately).

Control Support

Currently supports an arbitrary number of controlled things with independent mappings, chosen from the following types:

  • Linux backlight class. This gets proxied through logind to avoid requiring root privileges, as long as it's run by a user logged in at display.
  • Linux tpacpi (Thinkpad ACPI) keyboard backlight (in progress).

IPC Interface

The Walrus has an IPC interface that you can use to adjust its behavior. For example, if it has set the screen slightly dimmer than you prefer, you can send a command to adjust its decisions up slightly. The IPC interface is available over DBus, and the ambientwalrus ipc subcommand provides a convenient way of sending commands to a running copy of the Walrus. For more information, run ambientwalrus ipc help.

Specifically, these two commands are useful to bind to the "brightness adjust" keys on a keyboard:

ambientwalrus ipc up 0.1
ambientwalrus ipc down 0.1

Power and Wakes

ambient-walrus tries not to wake the CPU when nothing is changing. Sensor drivers use change notifications where possible, to avoid polling. Note that other processes required by the sensor driver may wake more often than this --- for instance, iio-sensors-proxy seems to wake about 2/sec when a sensor is being monitored.

During a lighting change, the wake rate is determined primarily by the controls.NAME.update-rate setting, and by default is about 60/s, stopping once brightness gets close to the new target value.

Configuration

The Walrus needs to be told what to do; you do this by providing it with a configuration file. The configuration file uses TOML syntax and is designed to have pretty general defaults, but allow you to adjust behaviors to suit your device.

The Walrus will look for configuration in the XDG config directories. Generally you probably want to place it in ~/.config/ambientwalrus/config.toml or, to share between users, /etc/xdg/ambientwalrus/config.toml. But you can also specify a different location using the -f/--config-file flag.

You can generate a basic config by running ambientwalrus generate. This will attempt to detect your devices and produce a valid starting configuration. You may need to edit the results to tweak them to your liking.

Here's a full description of the configuration options:

# The sensor section defines parameters of the light sensor. Currently there is
# only one light sensor.
[sensor]

# Chooses which sensor driver to use, and provides any additional settings for
# the driver. Currently the only supported driver is iio-sensors-proxy, which
# requires no driver-specific settings.
#
# Required.
driver = {kind = "iio-sensor-proxy"}

# Adjusts the input range for the sensor, using sensor units (often lux). By
# default, the lo and hi end of the range are detected where possible, but for
# lux sensors using Industrial I/O, there's currently no way to query the upper
# bound on the sensor, so you need to specify at least "hi".
#
# Overriding the lo setting to a higher number will cause the backlight to go
# "dimmest" at a higher level of illumination. Reducing the hi setting to a
# lower number will cause the backlight to go "brightest" at a lower level of
# illumination.
#
# Optional. You can also provide either lo or hi or both.
input = {lo = 0, hi = 1234}

# Polling rate for the sensor in Hz. This is how often the Walrus asks the
# driver, and has no direct influence on any internal polling or filtering in
# the sensor. Drivers are generally assumed to be providing a sensor-appropriate
# low-pass filter internally; if that needs configuration it'll be on the
# "driver" key above.
#
# Sensor drivers try not to require polling, so in practice this serves as a
# backup in the event that sensor change notifications stop flowing. Some
# sensor drivers might need polling, however, in which case you might need to
# adjust this up.
#
# Default: 0.2 (every 5 seconds)
poll-hz = 0.2

# Exponent for mapping sensed illumination to perceived brightness. Sensed
# illumination is generally a linear measure of luminous flux, but humans
# perceive brightness _exponentially._ If the controls seem to go bright or dim
# very fast and then stay there, this exponent might need tweaking (though note
# that each control _also_ has an exponent you can tweak).
#
# Default: 3
exponent = 3

# Each controls.NAME section defines some controlled device named NAME. The
# choice of NAME is up to you, and just serves to distinguish the controls from
# one another. Each NAME must be unique in the file.
#
# You can have any number of controls sections defining different devices.
[controls.NAME]
# Chooses which controller driver to use. Currently the main supported driver is
# "backlight", which requires an additional "device" key naming a device.
driver = {kind = "backlight", device = "amdgpu_bl1"}

# Adjusts the sensor range that this control responds to, on a scale from 0 to
# 1.
#
# Overriding the lo setting to a higher number will cause this control to go
# "dimmest" at a higher level of illumination. Reducing the hi setting to a
# lower number will cause this control to engage its "max-behavior" (see below)
# at a lower level of illumination.
#
# Optional. You can also provide either lo or hi or both.
# Default: {lo = 0, hi = 1}
input = {lo = 0, hi = 1}

# Adjusts the output range of this control that's actually used. The control
# will not be adjusted below the "lo" provided here, or above the "hi" provided
# here. Both values are on a scale from 0-1. So if you want to ensure that the
# display backlight never goes below 10% brightness, provide lo=0.1.
#
# Optional. You can also provide either lo or hi or both.
# Default: {lo = 0, hi = 1}
output = {lo = 0, hi = 1}

# Determines how the controlled device responds when its input signal meets or
# exceeds the setting for input.hi. Options include:
# - "saturate" (default): the control hangs out at its highest setting. This is
#   most suitable for display backlights.
# - "off": the control turns off. This is most suitable for supplemental
#   lighting like keyboards.
max-behavior = "saturate"

# Exponent for mapping control output to raw device values. Backlight and LED
# drivers generally directly control _current,_ and in the case of LEDs, current
# linearly maps to light output -- but humans don't perceive brightness
# linearly, but _exponentially._ If this control seems to go bright or dim very
# fast and then stay there, this exponent might need tweaking. If _all_ the
# controls behave this way, check the sensor.exponent instead.
#
# Default: 3
exponent = 3

# How fast this control will be adjusted, in fraction of its full range per
# second. So, a value of 1.0 will change by 100 percent per second, 50 percent
# in half a second, etc.
#
# Default: 0.5
adjust-slope = 0.5

# How often this control will be updated, in Hz. Higher numbers produce smoother
# fades, but will use more CPU. Not all controls can be updated quickly, and
# some (such as Thinkpad keyboard backlights) control fading internally. In
# those cases you'll want to adjust this down.
#
# Note that this only comes into play when the brightness is actually
# _changing._ Between changes, we don't update the device to save power.
#
# Default: 60
update-rate = 60