Automatically switches Windows desktop wallpapers based on configurable conditions (time, WiFi, location, day of week)
Project description
Wallpaper Auto
Automatically switches Windows desktop wallpapers based on configurable conditions (time, WiFi, day of week).
Features
- Multi-condition triggers: Supports Windows session changes, network changes, and time changes as three types of triggers
- Flexible rules: Supports AND/OR condition combinations, can evaluate multiple conditions simultaneously
- Condition types:
network: Current connected WiFi nameday_of_week_is: Day-of-week check (0=Monday ... 6=Sunday)time_range: Time range (supports crossing midnight)
- System tray control: System tray menu for manual wallpaper switching and pause/resume auto-switching
- Thread-safe: Each monitoring module runs independently without blocking others
- At-shutdown wallpaper: Optionally apply a specific wallpaper when Windows shuts down or the user logs off
Quick Start
-
Download the project to your local machine and enter the directory
-
Install the package:
pip install wallpaper-auto
-
Generate a starter config file:
wallpaper-auto init-config -
Edit config.yaml with your wallpaper paths and rules, then run:
wallpaper-auto
Configuration
Generate a starter config with all options documented:
wallpaper-auto init-config
# Creates config.yaml in the current directory
Or specify a custom path and force-overwrite an existing file:
wallpaper-auto init-config myconfig.yaml -f
See wallpaper-auto init-config --help for all options. You can also create a config.yaml file manually (or specify the path via -c).
Configuration Structure
# 1. Wallpaper resource pool
resource:
work_wallpaper: # Resource ID — referenced by rules & fallback
name: static_wallpaper # Single-image wallpaper
config:
path: "C:/path/to/wallpaper.jpg"
style: fill # fill / fit / stretch / center / tile
restore: false # restore original wallpaper on demount (default false)
carousel: # Multi-image cycling wallpaper (resource carousel)
name: resource_carousel
config:
resources: # Sub-resources to cycle through
- name: static_wallpaper
config: {path: "C:/Pictures/morning.jpg", style: fill}
- name: static_wallpaper
config: {path: "C:/Pictures/afternoon.jpg", style: fill}
interval: 300 # Seconds between switches (default 300)
random: false # true = random order, false = sequential
# 2. Trigger configuration
trigger:
- name: network # Monitor WiFi / network changes
config: {}
- name: time # Periodic time-based evaluation
config: {}
- name: windows_session # Monitor lock/unlock/logon/logoff
config: {}
# 3. Rules (evaluated top-to-bottom; first match wins)
rule:
- name: "At work"
condition:
wifi_ssid_is: "OfficeWiFi" # Leaf condition — evaluator name + params
target: "work_wallpaper"
- name: "Night mode"
condition:
and: # AND/OR combinators supported
- in_time_range: ["23:00", "06:00"]
- day_of_week_is: [0, 1, 2, 3, 4] # Monday to Friday
target: "dark_wallpaper"
# 4. Fallback wallpaper (used when no rule matches)
fallback: "default_wallpaper"
# 5. (Optional) At-shutdown wallpaper — applied when Windows shuts down
# at_shutdown: "work_wallpaper"
Running
# Generate a starter config file
wallpaper-auto init-config
# Use default config.yaml
wallpaper-auto
# Specify config file
wallpaper-auto -c /path/to/config.yaml
# Set log level
wallpaper-auto -l INFO
Or start programmatically from Python:
from wallpaper_auto import run_service
run_service("config.yaml")
Auto Start
To launch automatically at logon, create a Task Scheduler task with an At log on trigger. Use pythonw.exe to hide the console window:
How Config Parameters Flow to Components
Each component type can accept configuration via a config block in the YAML file. The key-value pairs are unpacked as keyword arguments to the component's constructor.
Triggers
trigger:
- name: time
config:
interval: 60 # → TimeTrigger(interval=60)
times: # → TimeTrigger(times=["09:00", "18:00"])
- "09:00"
- "18:00"
| Trigger | Constructor Parameters | Description |
|---|---|---|
time |
interval (seconds), times (list of "HH:MM" strings) |
Periodic polling interval and/or fixed daily trigger times |
network |
(none) | Fires on WiFi SSID changes |
windows_session |
(none) | Fires on lock/unlock/resume |
Resources
# Static — single image
resource:
custom_name:
name: static_wallpaper # Resource type → constructor lookup
config:
path: "C:/img.jpg" # → StaticWallpaper(path="C:/img.jpg", style="fill")
style: fill
# Resource carousel — cycles through multiple sub-resources on a timer
carousel:
name: resource_carousel
config:
resources:
- name: static_wallpaper
config: {path: "C:/img1.jpg", style: fill}
- name: static_wallpaper
config: {path: "C:/img2.jpg", style: fill}
interval: 300 # → ResourceCarousel(resources=[...], interval=300, random=False)
random: false
| Resource | Constructor Parameters | Description |
|---|---|---|
static_wallpaper |
path (str), style (str), restore (bool, default False), cache_dir (str, optional) |
Static image wallpaper — restore=True restores original wallpaper on demount. cache_dir overrides the auto-created temp directory. |
resource_carousel |
resources (list[dict]), interval (int, default 300), random (bool, default False) |
Cycles through sub-resources — each sub-resource is a full resource config dict with its own name and config. |
The shorthand form (black: "C:/img.jpg") is expanded to static_wallpaper with the string as the path.
Evaluators
Evaluators don't use a config block — their parameters are defined inline in the condition:
condition:
wifi_ssid_is: "Company_WiFi" # param: SSID string
in_time_range: ["09:00", "18:00"] # param: [start, end]
day_of_week_is: [5, 6] # param: list[int] 0=Mon ... 6=Sun
Custom Components
When registering a custom component, define __init__ parameters matching the keys you expect in the YAML config block:
class MyTrigger(BaseThreadTrigger):
def __init__(self, poll_interval: int = 30, endpoint: str = "..."):
...
System Tray
After running, the app displays an icon in the system tray:
- Auto mode: Resume auto-switching (hides the manual wallpaper options)
- Pause mode: Stop automatic rule-based switching. Manual wallpaper selection becomes available.
- Select wallpaper: Manually switch to the specified wallpaper (only available while paused)
Programmatic Usage
You can start the service from Python code using run_service(). This is the recommended way when registering custom components.
from wallpaper_auto import run_service
# Start with the default config.yaml
run_service("config.yaml")
# With custom components registered inline
run_service(
"config.yaml",
custom_triggers={"my_trigger": MyTrigger},
custom_resources={"my_resource": MyResource},
custom_evaluators={"my_evaluator": MyEvaluator()},
)
Custom Components
All three component types are extensible. The recommended way to register custom components is by passing them to run_service() via the custom_triggers, custom_resources, and custom_evaluators keyword arguments. All base classes are importable from the top-level wallpaper_auto package.
Custom Resource
Extend BaseResource and pass it through run_service().
from wallpaper_auto import BaseResource, run_service
class OnlineResource(BaseResource):
def __init__(self, query: str = "nature", style: str = "fill"):
self.query = query
self.style = style
def mount(self):
# Download image, then set as wallpaper
...
def demount(self):
# Restore previous wallpaper
...
run_service("config.yaml", custom_resources={"online": OnlineResource})
resource:
daily:
name: online
config:
query: "mountain"
style: fill
Custom Trigger
Extend BaseTrigger or BaseThreadTrigger and pass it through run_service().
from wallpaper_auto import BaseThreadTrigger, run_service
class UsbPlugTrigger(BaseThreadTrigger):
def run(self):
while not self._stop_event.is_set():
# Poll for USB insertion/removal
...
self.trigger()
self._stop_event.wait(timeout=5)
run_service("config.yaml", custom_triggers={"usb_plug": UsbPlugTrigger})
trigger:
- name: usb_plug
config: {}
Custom Evaluator
Implement BaseEvaluator (a callable interface) and pass an instance through run_service().
For example, here is a geo-location evaluator that checks whether the current machine is within a given radius of a target location:
from wallpaper_auto import BaseEvaluator, run_service
class GeoEvaluator(BaseEvaluator):
"""Evaluate whether the current machine is within a given radius
of a target location."""
def __call__(self, param: dict) -> bool:
# 1. Validate input: param must contain lat, lon, radius
# 2. Resolve current location via IP geolocation API
# 3. Compute distance between current location and target
# 4. Return True if distance <= radius
...
run_service("config.yaml", custom_evaluators={"in_geo_range": GeoEvaluator()})
The example above resolves the machine's public IP via a geolocation API, computes the distance using the Haversine formula, and returns True when the machine is within the configured radius. The actual API call and distance calculation are left as an exercise for the reader -- the pattern shown here is the extensibility contract: subclass BaseEvaluator, implement __call__, and pass an instance to run_service().
rule:
- name: "Near home"
condition:
in_geo_range:
lat: 31.23
lon: 121.47
radius: 0.5
target: "home_wallpaper"
Registering All Three Together
All custom component types can be registered in a single run_service() call:
from wallpaper_auto import (
BaseResource,
BaseThreadTrigger,
BaseEvaluator,
run_service,
)
class MyResource(BaseResource): ...
class MyTrigger(BaseThreadTrigger): ...
class MyEvaluator(BaseEvaluator): ...
run_service(
"config.yaml",
custom_triggers={"my_trigger": MyTrigger},
custom_resources={"my_resource": MyResource},
custom_evaluators={"in_geo_range": MyEvaluator()},
)
Alternative: Class-level Registration
As an alternative, you can register components directly on the manager classes before calling run_service(). This is useful when the registration must happen before the configuration is loaded (e.g., in a plugin system).
from wallpaper_auto import ResourceManager, RuleEngine, TriggerManager, run_service
ResourceManager.register_resource("online", OnlineResource)
TriggerManager.register_trigger("usb_plug", UsbPlugTrigger)
RuleEngine.register_evaluator("my_evaluator", MyEvaluator())
run_service("config.yaml")
Dependencies
- Python 3.12+
- PyYAML — config file parsing
- Pydantic >=2.0 — config validation & data models
- PySide6 — system tray UI
- pywin32 — Windows wallpaper API (SystemParametersInfo) & session monitoring
- Pillow — image resize/compress for wallpaper caching
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wallpaper_auto-1.0.0.tar.gz.
File metadata
- Download URL: wallpaper_auto-1.0.0.tar.gz
- Upload date:
- Size: 75.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bc76a870921ded70245759c6b33baac78cf5e30b68e4d6c6163ce22b36979a2
|
|
| MD5 |
5309b42ccd43a8569fbb0e8e48e2caa7
|
|
| BLAKE2b-256 |
fd6aaa10c0fb4ba0c79643546cce471150c69a6a479fafb2bf9d0fa7b168851d
|
Provenance
The following attestation bundles were made for wallpaper_auto-1.0.0.tar.gz:
Publisher:
publish.yml on HS-Mike/wallpaper-auto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wallpaper_auto-1.0.0.tar.gz -
Subject digest:
0bc76a870921ded70245759c6b33baac78cf5e30b68e4d6c6163ce22b36979a2 - Sigstore transparency entry: 1546927561
- Sigstore integration time:
-
Permalink:
HS-Mike/wallpaper-auto@4f400b1e6f1d4a963a1c5fc163bc4a81a5348881 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/HS-Mike
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4f400b1e6f1d4a963a1c5fc163bc4a81a5348881 -
Trigger Event:
push
-
Statement type:
File details
Details for the file wallpaper_auto-1.0.0-py3-none-any.whl.
File metadata
- Download URL: wallpaper_auto-1.0.0-py3-none-any.whl
- Upload date:
- Size: 42.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
899109b15d1b9401c911f59b55e408e057c7aa35c9e35ea335344c410b0092a4
|
|
| MD5 |
2a79c791375ee59a34f7dff061d27f49
|
|
| BLAKE2b-256 |
03aac7eb460115c6d19dec5f1b5f0da907cfa94cb4d67292ea4e79df9e09746e
|
Provenance
The following attestation bundles were made for wallpaper_auto-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on HS-Mike/wallpaper-auto
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wallpaper_auto-1.0.0-py3-none-any.whl -
Subject digest:
899109b15d1b9401c911f59b55e408e057c7aa35c9e35ea335344c410b0092a4 - Sigstore transparency entry: 1546927570
- Sigstore integration time:
-
Permalink:
HS-Mike/wallpaper-auto@4f400b1e6f1d4a963a1c5fc163bc4a81a5348881 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/HS-Mike
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4f400b1e6f1d4a963a1c5fc163bc4a81a5348881 -
Trigger Event:
push
-
Statement type: