How it works

Demo

Real-time wallpaper monitoring:

WallpaperSync uses Watchdog to monitor the system wallpaper from the default wallpaper path %appdata%/Microsoft/Windows/Themes/TranscodedWallpaper. The monitoring starts with the instantiation of the WallpaperHandler class

            
path_to_wallpaper = os.path.join(local_folder, 'TranscodedWallpaper.png')
print("Monitoring wallpaper changes...")
event_handler = WallpaperHandler(original_path, path_to_wallpaper, ip, port)
          

The Observer starts listening for modifications to the specified wallpaper file. Upon detecting changes, it triggers the on_modified method defined in the WallpaperHandler class.

            
observer = Observer()
observer.schedule(event_handler, path=os.path.dirname(original_path), recursive=False)
observer.start()
          

Color Palette Extraction:

After obtaining the new wallpaper, the script extracts the dominant colors using the ColorThief library. This functionality is encapsulated in the process_wallpaper function.

            
def process_wallpaper(path_to_wallpaper: str) -> Tuple[List[Tuple[int, int, int]], Tuple[int, int, int]]:
    color_thief = ColorThief(path_to_wallpaper)
    palette = color_thief.get_palette(color_count=6)
          

The ColorThief class is instantiated with the path to the wallpaper, and the get_palette method extracts a palette of colors. The palette is then filtered to exclude nearly white colors using the is_near_white function.

Color Adjustment:

            
def check_palette(palette: List[Tuple[int, int, int]], tolerance: float = 10.0, threshold: float = 50.0) -> str:

    ...
          

This function uses two inner functions, is_greyscale() and is_low_contrast(), to classify the palette based on defined thresholds.

Based on the classification, the script adjusts the selected colors. For instance, if the palette is classified as "greyscale," it sets a default color of white:

            
if palette_type == 'greyscale':
    adjusted_rgb1 = (255, 255, 255)  # White
          

If it is classified as "low contrast," it identifies the most saturated color and enhances its vibrance:

            
most_saturated_color = max(filtered_palette, key=get_saturation)
adjusted_rgb1 = adjust_saturation_vibrance(most_saturated_color)
          

Configuring LEDs:

The script utilizes the OpenRGB client to connect to the LED devices and configure their colors based on the extracted palette.

            
def configure_leds(adjusted_rgb1: Tuple[int, int, int], ip: str, port: int):
    client = OpenRGBClient(ip, port, 'Wal.py')
    client.connect()

    target_color = RGBColor(*adjusted_rgb1)
    fade_color_transition(client, target_color)
          

Here, the script creates a connection to the OpenRGB server using the specified IP address and port. It then calls the fade_color_transition function to smoothly transition the LED colors to match the wallpaper.

Fading Color Transition

            
def fade_color_transition(client: OpenRGBClient, target_color: RGBColor, duration: int = 1, steps: int = 20):
    current_colors = [device.colors[0] for device in client.devices]

...          
          

The fade_color_transition function gradually changes the LED colors from the current state to the new target color over a specified duration and number of steps

The function calculates the intermediate color for each step and applies it to all connected LED devices, creating a smooth fading effect.

Configuration Management

            
def load_config(config_file: str) -> Tuple[str, int]:
    if os.path.isfile(config_file):
        with open(config_file, 'r') as file:
            config = json.load(file)
            return config.get('ip', 'localhost'), config.get('port', 6742)
    return 'localhost', 6742
          

The script supports loading and saving configurations through JSON files, allowing users to persist their IP and port settings for the OpenRGB client

This function checks if a configuration file exists and reads the server settings if it does. If not, it defaults to 'localhost' and port 6742.