loader image

Logitech Litra Glow (Linux Drivers)

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

Viewing 9 posts - 1 through 9 (of 9 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, 6 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 1 month, 2 weeks ago by thumbtak.
    • This reply was modified 1 month, 2 weeks ago by thumbtak. Reason: Fixed layout of forum post
Viewing 9 posts - 1 through 9 (of 9 total)
  • You must be logged in to reply to this topic.
TAKs Shack