diff --git a/AGENTS.md b/AGENTS.md index 8042f9f..faf9a81 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,18 +11,9 @@ This repo is a single Python script (`luz-musica.py`) that: - Python deps: `requests`, `Pillow`. - Run: `python3 luz-musica.py` -## Dependencies (recommended setups) +## Dependencies -### Option A (NixOS / Nix): reproducible dev shell (no pip) -If you have Nix with flakes enabled: -- Enter shell: `nix develop` -- Run: `python3 luz-musica.py` - -Without flakes: -- Enter shell: `nix-shell` -- Run: `python3 luz-musica.py` - -### Option B (Ubuntu/Debian/etc): isolated venv (pip) +### Ubuntu/Debian/etc: isolated venv (pip) Install system bits: - `sudo apt-get update` - `sudo apt-get install -y python3 python3-venv playerctl` diff --git a/README.md b/README.md index a399055..66065ee 100644 --- a/README.md +++ b/README.md @@ -2,101 +2,62 @@ `luz-musica.py` changes a Home Assistant light to match the active media artwork. -## Home Manager integration +## Requirements -This repository includes a Home Manager module at [home-manager/music-light.nix](/home/ltadeu6/music-light/home-manager/music-light.nix) that creates: +- `python3` +- `python3-venv` +- `playerctl` +- Python packages from `requirements.txt` -- a `systemd --user` service called `music-light.service` -- helper commands: - - `toggle-music-light` - - `start-music-light` - - `stop-music-light` - - `music-light-status` -- an optional desktop launcher: `Music Light Toggle` - -### 1. Import the module - -Add this to your Home Manager configuration: - -```nix -{ - imports = [ - /home/vini/music-light/home-manager/music-light.nix - ]; - - programs.music-light = { - enable = true; - scriptDir = "/home/vini/music-light"; - pythonPath = "/usr/bin/python3"; - playerctlPath = "/usr/bin/playerctl"; - enableDesktopEntry = true; - autostart = false; - }; -} -``` - -If your Home Manager config is inside a flake, add the module to the relevant `homeManagerConfiguration`. - -### 2. Install the host runtime on Ubuntu - -On the Ubuntu host, install the runtime used by the service: +On Ubuntu/Debian: + +```bash +sudo apt-get update +sudo apt-get install -y python3 python3-venv playerctl +``` + +## Setup + +Create a virtual environment and install dependencies: ```bash -sudo apt update -sudo apt install -y python3 python3-venv playerctl -cd /home/vini/music-light python3 -m venv .venv -. .venv/bin/activate +source .venv/bin/activate +python -m pip install -U pip pip install -r requirements.txt ``` -Then point Home Manager at the venv interpreter: +## Configuration -```nix -programs.music-light.pythonPath = "/home/vini/music-light/.venv/bin/python"; -``` +Edit the constants at the top of `luz-musica.py`: -If you prefer system-wide Python packages instead of a venv, keep `pythonPath = "/usr/bin/python3"` and make sure `requests` and `Pillow` are installed on the host. +- `HA_URL` +- `HA_TOKEN` +- `LIGHT_ENTITY` -### 3. Apply Home Manager for the host user +You can also tune: -The service must run in the host user's `systemd --user`, not inside the Docker container. +- `BRIGHTNESS_PCT` +- `SATURATION_BOOST` +- `MIN_SATURATION` +- `MIN_VALUE` +- `REQUEST_TIMEOUT` -The Docker container can generate the Home Manager files as long as it writes into the real home directory used by the Ubuntu user. After activation, reload the user units on the host: +## Run ```bash -systemctl --user daemon-reload +source .venv/bin/activate +python luz-musica.py ``` -Then control it with: +Or use the Makefile: ```bash -toggle-music-light -music-light-status -start-music-light -stop-music-light -journalctl --user -u music-light.service -f +make run ``` -### 4. Desktop button +## Troubleshooting -With `enableDesktopEntry = true`, Home Manager creates a launcher named `Music Light Toggle`. - -It typically appears in: - -- the application menu -- launchers that read `.desktop` files -- `~/.local/share/applications/` - -The generated launcher simply runs `toggle-music-light`, which starts or stops `music-light.service`. - -## Important detail about Docker + Ubuntu host - -If Home Manager is executed inside a container, that is fine for generating the user configuration files, but: - -- the target home directory must be the real home directory used by the host user -- the service is started by the host's `systemd --user` -- `python3`, `playerctl`, the virtualenv, and the media-session DBus must all be available on the host -- the service must not point to executables that exist only inside the container or its `/nix/store` - -If `systemctl --user` inside the container does not control the host user session, run the final activation from the host side. +- If nothing is playing, or `playerctl` cannot read metadata, the loop sleeps and retries. +- If `mpris:artUrl` is missing, the script logs it and waits for the next track change. +- If Home Assistant is unreachable or returns an error, the script logs the exception and retries. diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 682004d..0000000 --- a/flake.lock +++ /dev/null @@ -1,27 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1776169885, - "narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 03c3a5b..0000000 --- a/flake.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ - description = "music-light: dev environment (Python + playerctl) for NixOS/Nix"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - }; - - outputs = { self, nixpkgs }: - let - systems = [ - "x86_64-linux" - "aarch64-linux" - ]; - - forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); - in - { - devShells = forAllSystems (system: - let - pkgs = import nixpkgs { inherit system; }; - - # Use a stable Python version with good binary coverage for Pillow. - py = pkgs.python312.withPackages (ps: [ - ps.requests - ps.pillow - ]); - in - { - default = pkgs.mkShell { - packages = [ - py - pkgs.playerctl - ]; - - shellHook = '' - echo "music-light devShell ready: $(python --version)" - ''; - }; - }); - }; -} - diff --git a/home-manager/music-light.nix b/home-manager/music-light.nix deleted file mode 100644 index d3f4318..0000000 --- a/home-manager/music-light.nix +++ /dev/null @@ -1,108 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.programs.music-light; - - toggleScript = pkgs.writeShellScriptBin "toggle-music-light" '' - if systemctl --user is-active --quiet music-light.service; then - exec systemctl --user stop music-light.service - else - exec systemctl --user start music-light.service - fi - ''; - - startScript = pkgs.writeShellScriptBin "start-music-light" '' - exec systemctl --user start music-light.service - ''; - - stopScript = pkgs.writeShellScriptBin "stop-music-light" '' - exec systemctl --user stop music-light.service - ''; - - statusScript = pkgs.writeShellScriptBin "music-light-status" '' - if systemctl --user is-active --quiet music-light.service; then - echo on - else - echo off - fi - ''; -in -{ - options.programs.music-light = { - enable = lib.mkEnableOption "music-light integration managed by Home Manager"; - - scriptDir = lib.mkOption { - type = lib.types.str; - default = "${config.home.homeDirectory}/music-light"; - example = "/home/vini/music-light"; - description = "Directory that contains `luz-musica.py`."; - }; - - pythonPath = lib.mkOption { - type = lib.types.str; - default = "/usr/bin/python3"; - example = "/usr/bin/python3"; - description = "Path to the host Python interpreter used by the service."; - }; - - playerctlPath = lib.mkOption { - type = lib.types.str; - default = "/usr/bin/playerctl"; - example = "/usr/bin/playerctl"; - description = "Path to the host `playerctl` binary used by the service."; - }; - - enableDesktopEntry = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to create a desktop launcher that toggles the service."; - }; - - autostart = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to start `music-light.service` automatically at login."; - }; - }; - - config = lib.mkIf cfg.enable { - home.packages = [ - toggleScript - startScript - stopScript - statusScript - ]; - - xdg.desktopEntries = lib.mkIf cfg.enableDesktopEntry { - music-light-toggle = { - name = "Music Light Toggle"; - comment = "Start or stop the music-light sync service"; - exec = "${toggleScript}/bin/toggle-music-light"; - terminal = false; - categories = [ "Utility" ]; - }; - }; - - systemd.user.services.music-light = { - Unit = { - Description = "Sync Home Assistant light color with the active media artwork"; - After = [ "graphical-session.target" "network-online.target" ]; - PartOf = [ "graphical-session.target" ]; - }; - - Service = { - Type = "simple"; - WorkingDirectory = cfg.scriptDir; - ExecStart = "${cfg.pythonPath} ${cfg.scriptDir}/luz-musica.py"; - Restart = "on-failure"; - RestartSec = 3; - Environment = [ - "PATH=/usr/local/bin:/usr/bin:/bin" - "PLAYERCTL_BIN=${cfg.playerctlPath}" - ]; - }; - - Install.WantedBy = lib.mkIf cfg.autostart [ "default.target" ]; - }; - }; -} diff --git a/luz-musica.py b/luz-musica.py index 35582f8..e228651 100644 --- a/luz-musica.py +++ b/luz-musica.py @@ -12,7 +12,7 @@ from urllib.parse import parse_qs, urlparse import requests from PIL import Image -HA_URL = "http://192.168.1.105:8123" +HA_URL = "http://127.0.0.1:8123" HA_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkZjM1MmRhNjg1ZWM0MGJiYTc4YTZlNWY3ZmQ0YTA1YSIsImlhdCI6MTc3NjI4MTI0MiwiZXhwIjoyMDkxNjQxMjQyfQ.rR0Kfwk5Smr06_9KJSDn3YPXiRlokEZZFiAa_naNs78" LIGHT_ENTITY = "light.luzsala_luz_sala" diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 4e3fe4d..0000000 --- a/shell.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ pkgs ? import { } }: -let - py = pkgs.python312.withPackages (ps: [ - ps.requests - ps.pillow - ]); -in -pkgs.mkShell { - packages = [ - py - pkgs.playerctl - ]; -} -