loader image

Logitech Litra Glow (Linux Drivers)

What makes us different from other similar websites? Forums Tech Logitech Litra Glow (Linux Drivers)

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • #6670
    thumbtak
    Moderator


    I just got the Logitech Litra Glow, well, I am waiting for it in the mail. While I wait for it, I wanted to get the drivers I found, working on my Linux machine. I got it to work. This is how.

    $ pip install litra-driver

    $ sudo echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c900",\
    MODE="0666"' > /etc/udev/rules.d/82-litra-glow.rules

    $ sudo shutdown -r now

    $ lcui

    If you get this error message “ModuleNotFoundError: No module named ‘Tkinter” continue on, if not, you are done.

    $ sudo apt-get install python3-tk

    $ lcui

    More information:

    https://github.com/derickr/python-litra-driver

    https://stackoverflow.com/questions/25905540/importerror-no-module-named-tkinter

    Amazon Product Link

    #6671
    thumbtak
    Moderator

    I made an icon that you may use when adding the terminal command to the application menu.

    #6672
    thumbtak
    Moderator

    I found a bug in the program. The bug is with clicking “X” to close the window instead of “exit”. When you delete a profile, the “X” will not save the deleted profiles. The profile will return when you open the application. To remove them, you must click “exit”, after deleting them.

    #6728
    thumbtak
    Moderator

    If you are getting this error message, install the following.

    $ sudo apt install gir1.2-appindicator3-0.1

    #6729
    thumbtak
    Moderator

    If you get a permission denied when doing this command …

    $ echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c900",\ MODE="0666"' > /etc/udev/rules.d/82-litra-glow.rules

    Run the following command and give Others, Read & Write access to the 82-litra-glow.rules file located in the /etc/udev/rules.d/ by running the following command …

    $ sudo tee /etc/udev/rules.d/82-litra-glow.rules <<< 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c900",MODE="0666"'

    #6730
    thumbtak
    Moderator

    If you are getting this error …

    https://i.ibb.co/3zFJ20q/image.pngimage

    Run the following command …
    $ sudo lcui

    • This reply was modified 3 years, 10 months ago by thumbtak.
    #6785
    thumbtak
    Moderator

    If you get this error:

    ModuleNotFoundError: No module named 'tkinter'

    Run this command:

    
    $ sudo apt-get install python3-tk

    #7669
    thumbtak
    Moderator

    How to install on later versions of Linux

    $ sudo tee /etc/udev/rules.d/82-litra-glow.rules <<< 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c900",MODE="0666"'

    $ sudo tee /etc/udev/rules.d/82-litra-beam.rules <<< 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c901",MODE="0666"'

    $ sudo udevadm control --reload-rules

    $ sudo udevadm trigger

    $ sudo reboot

    $ python3 -m venv litra

    $ source litra/bin/activate

    $ pip install litra-driver

    $ lcui

    You may use this script and make a shortcut on your panel to start the software. It may need modifying, depending on where you place the files. I out the environment in my home directory.

    #!/bin/sh
    
    # Navigate to the home directory, or the directory where your virtual environment is
    cd ~/litra || exit # If this fails, exit the script
    
    # Activate the virtual environment
    if [ -f "bin/activate" ]; then
    . bin/activate # Use '.' (dot) instead of 'source' in sh
    else
    echo "Virtual environment not found. Exiting."
    exit 1
    fi
    
    # Run open-webui
    lcui
    
    # Notify user the server is up
    echo "lcui should be opening"

    The way I set it up in the launcher, on my panel is by copying the sh file and post it in the command section, with sh before the location of the file.

    #8344
    thumbtak
    Moderator

    💡 Litra Light Controller GUI Setup Guide for Linux (Xubuntu/Debian)

    This guide provides every single step needed to install the lcli command-line tool, set up the Python GUI, and create a custom menu entry for controlling your Logitech Litra light.

    1. Prerequisites and Installing the CLI Tool (lcli)

    This application requires Python 3, Tkinter, and the external lcli tool.

    1.1 Download the Litra CLI Tool

    1. Open your web browser and go to the official lcli GitHub repository Releases page:

    https://github.com/charles-dyer/lcli/releases

    2. Download the latest Linux x86_64 release (it will be named something like lcli_x86_64.tar.gz).

    3. Open your terminal and navigate to your Downloads directory:

    cd ~/Downloads

    4. Extract the contents (replace [FILENAME] with the actual file name you downloaded, e.g., lcli_x86_64.tar.gz):

    tar -xzf [FILENAME]

    5. Move the executable to a standard binary location so you know its path. Assuming the extracted executable is named lcli:

    sudo mv ./lcli /usr/local/bin/lcli

    If this command fails because the file is in a subdirectory, locate the lcli executable inside the extracted folder and move it to /usr/local/bin/.

    1.2 Identify the Absolute Path

    The absolute path for the lcli executable is now: /usr/local/bin/lcli. You will use this path in the Python script in the next step.

    1.3 Python and Tkinter Check

    Ensure you have Python 3 and Tkinter installed. If you are on a Debian/Ubuntu-based system and the GUI doesn’t work, install Tkinter:

    sudo apt update
    sudo apt install python3-tk

    2. Setup the Application Files

    We will create a specific directory to hold the Python script. Open your terminal and run:

    mkdir -p ~/.local/share/litra-controller

    2.A Create the GUI Python Script (litra_controller.py)

    This is the main application code. Open your preferred text editor (mousepad) and save the following content as ~/.local/share/litra-controller/litra_controller.py.

    CRITICAL: The DEFAULT_CLI_PATH in the code below is set to /usr/local/bin/lcli. Only change it if you moved the executable elsewhere.

    import tkinter as tk
    from tkinter import ttk, messagebox
    import subprocess
    import webbrowser 
    
    # --- Configuration Constants ---
    # ABSOLUTE PATH TO THE EXECUTABLE.
    DEFAULT_CLI_PATH = '/usr/local/bin/lcli'
    MIN_BRIGHTNESS = 1
    MAX_BRIGHTNESS = 100
    MIN_TEMP = 2700
    MAX_TEMP = 6500
    HIGHLIGHT_COLOR = '#ff66cc'
    
    class LitraControllerApp(tk.Tk):
        def __init__(self):
            super().__init__()
            self.title("Litra Light Controller (Python CLI Wrapper)")
            self.geometry("520x450")
            self.resizable(False, False)
            
            try:
                ttk.Style().theme_use('clam')
            except:
                pass
            
            self.cli_path = tk.StringVar(value=DEFAULT_CLI_PATH)
            self.is_power_on = tk.BooleanVar(value=True)
            self.generated_command = tk.StringVar(value="Adjust controls or click a button to generate command.")
            
            self.create_menu()
            self.create_widgets()
            self.update_command(None)
    
        def create_menu(self):
            menubar = tk.Menu(self)
            self.config(menu=menubar)
            help_menu = tk.Menu(menubar, tearoff=0)
            menubar.add_cascade(label="Help", menu=help_menu)
            help_menu.add_command(label="About", command=self.show_about_dialog)
    
        def open_url(self, url):
            webbrowser.open_new_tab(url)
    
        def show_about_dialog(self):
            about_window = tk.Toplevel(self)
            about_window.title("About Litra Controller")
            about_window.resizable(False, False)
            about_window.transient(self)
            about_window.grab_set() 
    
            frame = ttk.Frame(about_window, padding="15")
            frame.pack(fill="both", expand=True)
    
            ttk.Label(frame, text="Litra Light Controller", font=('Arial', 14, 'bold')).pack(pady=(0, 5))
            ttk.Label(frame, text="Version 1.0 (Python CLI Wrapper)", font=('Arial', 10)).pack(pady=(0, 10))
    
            description = (
                "This GUI allows control of your Logitech Litra light via the "
                "external Litra CLI (lcli) tool, requiring sudo permissions."
            )
            ttk.Label(frame, text=description, wraplength=350, justify=tk.CENTER).pack(pady=(0, 20))
    
            ttk.Label(frame, text="GUI Development:", font=('Arial', 10, 'bold')).pack()
            
            link_text = "thumbtak on taksshack.com"
            url = "[https://taksshack.com/members/thumbtak/](https://taksshack.com/members/thumbtak/)"
            
            link_label = ttk.Label(frame, text=link_text, 
                                   foreground="blue", 
                                   cursor="hand2", 
                                   font=('Arial', 10, 'underline'))
            link_label.pack(pady=(0, 5))
            link_label.bind("<Button-1>", lambda e: self.open_url(url))
    
            ttk.Button(frame, text="Close", command=about_window.destroy, style='Pink.TButton').pack(pady=(15, 0))
    
            about_window.update_idletasks()
            main_x = self.winfo_x(); main_y = self.winfo_y()
            main_width = self.winfo_width(); main_height = self.winfo_height()
            win_width = about_window.winfo_reqwidth(); win_height = about_window.winfo_reqheight()
            x = main_x + (main_width // 2) - (win_width // 2)
            y = main_y + (main_height // 2) - (win_height // 2)
            about_window.geometry(f'+{x}+{y}')
            
            self.wait_window(about_window)
    
        def create_widgets(self):
            # Widget creation logic (omitted for brevity, see code for full implementation)
            main_frame = ttk.Frame(self, padding="15")
            main_frame.pack(fill='both', expand=True)
    
            ttk.Label(main_frame, text="Litra CLI Path (lcli):", font=('Arial', 10, 'bold')).pack(pady=(0, 2), anchor='w')
            ttk.Entry(main_frame, textvariable=self.cli_path).pack(fill='x', padx=5, pady=(0, 10))
    
            control_frame = ttk.LabelFrame(main_frame, text="Light Controls", padding="10")
            control_frame.pack(fill='x', pady=10)
    
            power_frame = ttk.Frame(control_frame)
            power_frame.pack(fill='x', pady=5)
            ttk.Label(power_frame, text="Light Power:", font=('Arial', 11)).pack(side='left')
            
            self.power_button = ttk.Button(power_frame, text="", command=self.toggle_power)
            self.power_button.pack(side='right')
    
            ttk.Label(control_frame, text="Brightness:", font=('Arial', 11)).pack(pady=(10, 0), anchor='w')
            self.brightness_scale = tk.Scale(control_frame, from_=MIN_BRIGHTNESS, to=MAX_BRIGHTNESS, 
                                             orient=tk.HORIZONTAL, command=self.update_command,
                                             length=400, resolution=1, relief=tk.FLAT, bd=0, 
                                             troughcolor="#E0E7FF", showvalue=True, highlightthickness=0)
            self.brightness_scale.set(50)
            self.brightness_scale.pack(fill='x', padx=5)
            
            ttk.Label(control_frame, text="Color Temperature (K):", font=('Arial', 11)).pack(pady=(10, 0), anchor='w')
            self.temp_scale = tk.Scale(control_frame, from_=MIN_TEMP, to=MAX_TEMP, 
                                       orient=tk.HORIZONTAL, command=self.update_command,
                                       length=400, resolution=50, relief=tk.FLAT, bd=0,
                                       troughcolor="#E0E7FF", showvalue=True, highlightthickness=0)
            self.temp_scale.set(4600)
            self.temp_scale.pack(fill='x', padx=5)
    
            ttk.Label(main_frame, text="Generated Command:", font=('Arial', 10, 'bold')).pack(pady=(15, 2), anchor='w')
            
            command_output_frame = ttk.Frame(main_frame)
            command_output_frame.pack(fill='x')
            command_output_frame.columnconfigure(0, weight=1)
            
            self.command_text = tk.Text(command_output_frame, height=2, width=50, state='disabled', 
                                        bg="#2D3748", fg=HIGHLIGHT_COLOR, font=('Courier', 10), wrap='word', bd=1, relief=tk.SUNKEN) 
            self.command_text.grid(row=0, column=0, sticky="nsew", padx=(0, 5), pady=0)
            
            copy_button = ttk.Button(command_output_frame, text="Copy", command=self.copy_command)
            copy_button.grid(row=0, column=1, sticky="ns", pady=0)
            
            self.update_power_button_ui()
    
        def update_power_button_ui(self):
            if self.is_power_on.get():
                self.power_button.config(text=" ON ", style='Pink.TButton')
            else:
                self.power_button.config(text=" OFF ", style='Red.TButton')
                
            s = ttk.Style()
            s.configure('Pink.TButton', background=HIGHLIGHT_COLOR, foreground='black', font=('Arial', 10, 'bold'))
            s.map('Pink.TButton', background=[('active', '#e65cb8')]) 
            s.configure('Red.TButton', background='#F56565', foreground='black', font=('Arial', 10, 'bold'))
            s.map('Red.TButton', background=[('active', '#E53E3E')])
            
        def toggle_power(self):
            self.is_power_on.set(not self.is_power_on.get())
            self.update_power_button_ui()
            self.update_command(None)
    
        def update_command(self, event):
            cli = self.cli_path.get().strip()
            brightness = self.brightness_scale.get()
            temperature = self.temp_scale.get()
            command_parts = []
            sudo_cli = f"sudo {cli}" 
            
            if self.is_power_on.get():
                command_parts.append(f"{sudo_cli} on")
                command_parts.append(f"{sudo_cli} bright {brightness}")
                command_parts.append(f"{sudo_cli} temp {temperature}")
                command_string = " && ".join(command_parts)
            else:
                command_string = f"{sudo_cli} off"
    
            self.command_text.config(state='normal')
            self.command_text.delete(1.0, tk.END)
            self.command_text.insert(tk.END, command_string)
            self.command_text.config(state='disabled')
            
            self.execute_command(auto_exec=True)
            
        def copy_command(self):
            command = self.command_text.get(1.0, tk.END).strip()
            if command:
                self.clipboard_clear()
                self.clipboard_append(command)
                messagebox.showinfo("Copied", "Command copied to clipboard!")
            else:
                messagebox.showwarning("Empty", "No command to copy.")
    
        def execute_command(self, auto_exec=False):
            command = self.command_text.get(1.0, tk.END).strip()
            if not command: return
    
            try:
                if not auto_exec:
                    messagebox.showinfo("Executing Command", 
                                    "Running command in shell. You will likely be prompted for your sudo password in the terminal window where you launched this GUI.")
                
                result = subprocess.run(command, shell=True, check=True, 
                                        capture_output=True, text=True)
                
                if auto_exec:
                    print(f"Executed: {command}")
                else:
                    output = result.stdout.strip() if result.stdout else "Command executed successfully with no output."
                    messagebox.showinfo("Execution Success", f"SUCCESS:\n{output}")
                
            except subprocess.CalledProcessError as e:
                error_output = e.stderr.strip() if e.stderr else "Unknown error."
                messagebox.showerror("Execution Failed", f"Command failed with return code {e.returncode}. You may need to enter your sudo password in the terminal:\n{error_output}")
            
            except FileNotFoundError:
                messagebox.showerror("Execution Failed", f"The executable specified was not found. Check the CLI Path: '{self.cli_path.get()}'")
                
            except Exception as e:
                messagebox.showerror("Execution Failed", f"An unexpected error occurred:\n{e}")
    
    if __name__ == "__main__":
        app = LitraControllerApp()
        app.mainloop()

    2.B Create the Runner Script (run_litra.sh)

    This shell script launches the Python GUI using a terminal, which is necessary to handle the sudo password prompt.

    1. Create the script file using mousepad:

    mkdir -p ~/.local/bin/
    mousepad ~/.local/bin/run_litra.sh

    2. Paste the following content into the file:

    #!/bin/bash
    
    # Define the absolute path to the Python script using the user's HOME directory
    SCRIPT_PATH="$HOME/.local/share/litra-controller/litra_controller.py"
    
    # Execute the script using the default terminal emulator (e.g., xfce4-terminal).
    # The --hold option keeps the terminal open to capture the sudo password and see errors.
    xfce4-terminal --hold --title="Litra Controller Runner" --command="python3 $SCRIPT_PATH"

    3. Set Permissions: Make the runner script executable:

    chmod +x ~/.local/bin/run_litra.sh

    3. Create the Desktop Launcher (litra-controller.desktop)

    This file integrates the application into your desktop environment’s application menu (e.g., the Start Menu).

    1. Create the file using mousepad:

    mousepad ~/.local/share/applications/litra-controller.desktop

    2. Paste the following content into the file:

    [Desktop Entry]
    Name=Litra Light Controller
    Comment=GUI to control Logitech Litra light via lcli
    Exec=$HOME/.local/bin/run_litra.sh
    Icon=system-run
    Terminal=false
    Type=Application
    Categories=Utility;

    3. Save and close mousepad.

    4. Final Steps and Usage

    1. Refresh Menu: Log out and log back into your desktop session to ensure the application menu refreshes and shows the new entry.

    2. Launch: Find “Litra Light Controller” in your application menu and click it. A terminal window will open, and the separate GUI window will launch.

    3. Sudo Prompt: The first time you adjust a control (like brightness or color temperature), the terminal window will prompt you for your user password with:

    [sudo] password for yourusername:

    4. Enter Password: Type your user password into the terminal and press Enter.

    5. Enjoy: Once authenticated, the application will run all commands successfully until the sudo timeout expires.

    • This reply was modified 5 months, 1 week ago by thumbtak.
    • This reply was modified 5 months, 1 week ago by thumbtak. Reason: Fixed layout of forum post
    #8480
    thumbtak
    Moderator

    Guide: How to Setup and Build Litra CLI (lcli) on Xubuntu / Ubuntu

    If you are using a Logitech Litra Glow or Beam on an Ubuntu-based system and want to use community tools like lcli (or build a custom Python Tkinter GUI wrapper for it), you will need to compile the binary from source and set the correct hardware permissions.

    Here is the complete sequence of terminal commands to get everything working from scratch.

    1. Install System Dependencies
    First, install the Python Tkinter library (if you plan to run a GUI), the Go compiler, and the necessary C-development headers for USB device communication (libudev):

    $ sudo apt update
    $ sudo apt install python3-tk golang-go libudev-dev

    2. Download and Extract the Source Code
    Navigate to your Downloads folder, download the source code zip from the repository, and unzip it:

    $ cd ~/Downloads
    $ wget https://github.com/kharyam/go-litra-driver/archive/refs/heads/main.zip
    $ unzip main.zip

    3. Compile the Binary
    Navigate into the lcli source directory and compile the program using Go. (Note: If the build completes successfully, the terminal will return to a blank line without printing any text):

    $ cd go-litra-driver-main/lcli
    $ go build

    4. Install lcli Globally
    Move the newly compiled executable file into your system’s global binary folder so it can be run from anywhere, then grant it execution permissions:

    $ sudo cp lcli /usr/local/bin/lcli
    $ sudo chmod +x /usr/local/bin/lcli

    5. Configure USB Permissions (udev Rule)
    By default, Linux restricts raw access to USB devices. Create a udev rule so your user account can communicate with the Litra device without needing sudo:

    $ sudo tee /etc/udev/rules.d/82-litra-glow.rules <<< 'SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c900", MODE="0666"'

    (Note: If you are using a Litra Beam instead of a Glow, change c900 to c901)

    6. Apply Rules and Test
    Reload the system rules to apply the changes, and unplug/replug your Litra light’s USB cable. Then, test that the command works directly from the terminal:

    $ sudo udevadm control --reload-rules && sudo udevadm trigger
    $ lcli on

    7. Run Your Python GUI (Refer to 8 for the program code)
    Go back to your main directory and launch your Python controller script:

    import tkinter as tk
    from tkinter import ttk, simpledialog
    import subprocess
    
    # --- Configuration Constants ---
    DEFAULT_CLI_PATH = '/usr/local/bin/lcli'
    MIN_BRIGHTNESS = 1
    MAX_BRIGHTNESS = 100
    MIN_TEMP = 2700
    MAX_TEMP = 6500
    
    # High-Contrast Pastel Palette (No Greens/Teals)
    BG_PASTEL_MAIN = "#FFF0F5" # Lavender Blush / Soft Pink Tint
    BG_PASTEL_CARD = "#FFFFFF" # Crisp White for clean contrast
    TEXT_MAIN = "#3B203E" # Deepened Dark Plum for maximum readability
    TEXT_MUTED = "#7D5C86" # Darker Mauve for headers to ensure visibility
    ACCENT_PINK = "#FF94B8" # Slightly richer pastel pink for status text/active buttons
    SLIDER_TROUGH = "#FCD7E4" # Highly visible pink tint for slider tracks
    
    class LitraGlowStudio(tk.Tk):
    def __init__(self):
    super().__init__()
    self.title("Litra Glow Studio")
    self.geometry("340x290") # Sized slightly up to prevent text cramping
    self.resizable(False, False)
    self.configure(bg=BG_PASTEL_MAIN)
    
    self.cli_path = DEFAULT_CLI_PATH
    self.is_power_on = tk.BooleanVar(value=True)
    
    # Debounce tracking to keep live slider streaming fluid
    self.pending_update = None
    
    self.setup_styles()
    self.create_widgets()
    self.update_hardware(trigger_execution=False)
    
    def setup_styles(self):
    s = ttk.Style()
    try:
    s.theme_use('clam')
    except:
    pass
    
    s.configure('.', background=BG_PASTEL_MAIN, foreground=TEXT_MAIN)
    s.configure('TFrame', background=BG_PASTEL_MAIN)
    s.configure('Card.TFrame', background=BG_PASTEL_CARD)
    
    # High-contrast text on the buttons
    s.configure('PowerOn.TButton', background=ACCENT_PINK, foreground="#FFFFFF", font=('Arial', 10, 'bold'), borderwidth=0)
    s.map('PowerOn.TButton', background=[('active', '#FF709F')])
    
    s.configure('PowerOff.TButton', background=BG_PASTEL_CARD, foreground=TEXT_MUTED, font=('Arial', 10, 'bold'), borderwidth=1, bordercolor=SLIDER_TROUGH)
    s.map('PowerOff.TButton', background=[('active', BG_PASTEL_MAIN)])
    
    s.configure('Settings.TButton', background=BG_PASTEL_MAIN, foreground=TEXT_MUTED, font=('Arial', 11), borderwidth=0)
    s.map('Settings.TButton', background=[('active', BG_PASTEL_CARD)], foreground=[('active', TEXT_MAIN)])
    
    def create_widgets(self):
    # Header Area
    header_frame = ttk.Frame(self, padding=(15, 10, 15, 5))
    header_frame.pack(fill='x')
    
    title_label = tk.Label(header_frame, text="Litra Glow Studio", font=('Arial', 13, 'bold'), bg=BG_PASTEL_MAIN, fg=TEXT_MAIN)
    title_label.pack(side='left')
    
    settings_btn = ttk.Button(header_frame, text="⚙", width=3, style='Settings.TButton', command=self.open_settings)
    settings_btn.pack(side='right')
    
    # Main Control Card
    card_frame = ttk.Frame(self, padding="15", style='Card.TFrame')
    card_frame.pack(fill='both', expand=True, padx=15, pady=(5, 15))
    
    # Row 1: Power Status
    power_frame = tk.Frame(card_frame, bg=BG_PASTEL_CARD)
    power_frame.pack(fill='x', pady=(0, 10))
    
    self.status_label = tk.Label(power_frame, text="Light is On ✨", font=('Arial', 11, 'bold'), bg=BG_PASTEL_CARD, fg=ACCENT_PINK)
    self.status_label.pack(side='left', anchor='w')
    
    self.power_btn = ttk.Button(power_frame, text="ON", width=8, command=self.toggle_power)
    self.power_btn.pack(side='right')
    
    # Shared slider configurations (Darkened values for easier reading)
    slider_opts = {
    "orient": tk.HORIZONTAL, "bg": BG_PASTEL_CARD, "fg": TEXT_MAIN, "troughcolor": SLIDER_TROUGH,
    "highlightthickness": 0, "bd": 0, "activebackground": ACCENT_PINK, "font": ('Arial', 9, 'bold')
    }
    
    # Row 2: Live Brightness Slider
    tk.Label(card_frame, text="BRIGHTNESS", font=('Arial', 9, 'bold'), bg=BG_PASTEL_CARD, fg=TEXT_MUTED).pack(anchor='w', pady=(5, 0))
    self.bright_scale = tk.Scale(card_frame, from_=MIN_BRIGHTNESS, to=MAX_BRIGHTNESS, resolution=1,
    command=self.on_slider_move, **slider_opts)
    self.bright_scale.set(50)
    self.bright_scale.pack(fill='x', pady=(0, 8))
    
    # Row 3: Live Color Temperature Slider
    tk.Label(card_frame, text="COLOR TEMPERATURE (K)", font=('Arial', 9, 'bold'), bg=BG_PASTEL_CARD, fg=TEXT_MUTED).pack(anchor='w', pady=(5, 0))
    self.temp_scale = tk.Scale(card_frame, from_=MIN_TEMP, to=MAX_TEMP, resolution=50,
    command=self.on_slider_move, **slider_opts)
    self.temp_scale.set(4600)
    self.temp_scale.pack(fill='x', pady=(0, 15))
    
    # Row 4: Professional Bottom Branding Line
    branding_label = tk.Label(card_frame, text="created by thumbtak on TAKsShack.com",
    font=('Arial', 8, 'italic'), bg=BG_PASTEL_CARD, fg=TEXT_MUTED)
    branding_label.pack(side='bottom', pady=(5, 0))
    
    self.update_ui_state()
    
    def toggle_power(self):
    self.is_power_on.set(not self.is_power_on.get())
    self.update_ui_state()
    self.update_hardware(trigger_execution=True)
    
    def update_ui_state(self):
    if self.is_power_on.get():
    self.power_btn.config(text="ON", style='PowerOn.TButton')
    self.status_label.config(text="Light is On ✨", fg=ACCENT_PINK)
    self.bright_scale.config(state='normal', fg=TEXT_MAIN)
    self.temp_scale.config(state='normal', fg=TEXT_MAIN)
    else:
    self.power_btn.config(text="OFF", style='PowerOff.TButton')
    self.status_label.config(text="Sleeping 💤", fg=TEXT_MUTED)
    self.bright_scale.config(state='disabled', fg=SLIDER_TROUGH)
    self.temp_scale.config(state='disabled', fg=SLIDER_TROUGH)
    
    def open_settings(self):
    new_path = simpledialog.askstring("CLI Settings", "Modify global 'lcli' execution path:", initialvalue=self.cli_path, parent=self)
    if new_path:
    self.cli_path = new_path.strip()
    
    def on_slider_move(self, value):
    if self.pending_update:
    self.after_cancel(self.pending_update)
    self.pending_update = self.after(50, lambda: self.update_hardware(trigger_execution=True))
    
    def update_hardware(self, trigger_execution=False):
    if not trigger_execution:
    return
    
    brightness = self.bright_scale.get()
    temperature = self.temp_scale.get()
    
    if self.is_power_on.get():
    command = f"{self.cli_path} on && {self.cli_path} bright {brightness} && {self.cli_path} temp {temperature}"
    else:
    command = f"{self.cli_path} off"
    
    try:
    subprocess.Popen(command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    except Exception:
    pass
    
    if __name__ == "__main__":
    app = LitraGlowStudio()
    app.mainloop()
    • This reply was modified 23 hours, 48 minutes ago by thumbtak. Reason: Updated Program
Viewing 10 posts - 1 through 10 (of 10 total)
  • You must be logged in to reply to this topic.
TAKs Shack