From fd5bc47cb746da3e98b24162cac5948da344a45f Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Mon, 13 Sep 2021 19:18:14 +0200 Subject: [PATCH] basic formatting --- .gitignore | 3 ++- .vscode/settings.json | 3 +++ README.md | 11 +++++++++ index.ts | 28 ----------------------- joystick.ts | 2 +- package.json | 8 +++++-- spotify.ts | 21 ------------------ spotifyColors/index.ts | 48 ++++++++++++++++++++++++++++++++++++++++ spotifyColors/spotify.ts | 37 +++++++++++++++++++++++++++++++ tsconfig.json | 11 +++++++-- utils.ts | 18 ++++++++++++--- 11 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 README.md delete mode 100644 index.ts delete mode 100644 spotify.ts create mode 100644 spotifyColors/index.ts create mode 100644 spotifyColors/spotify.ts diff --git a/.gitignore b/.gitignore index 1b2ee77..54c7ab9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules -/pnpm-lock.yaml \ No newline at end of file +/pnpm-lock.yaml +dist \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..78c03fc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.preferences.importModuleSpecifierEnding": "js" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a91b5d0 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# PS4 controller colors + +I had the great idea to programatically set colors of my PS4 controller. + +It's not supposed to be easy to setup, but if you need help just contact me. For now, scripts try to find all the connected joy sticks (doesn't filter by type) and set their LEDs (No idea what happens if a joystick doesn't have one), and then sets colors on them. + +Project highly depends on linux (uses sys class to control the LEDs) and requires root (non-root operations are setuid'd internally). + +## Spotify colors + +Uses DBUS which needs your username. For now just edit the utils function getUsername to return your username. diff --git a/index.ts b/index.ts deleted file mode 100644 index 42f94f8..0000000 --- a/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { JoyStick } from "./joystick"; - -const devices = await JoyStick.getList(); - -for(const device of devices) { - const colors = await device.getColors(); - const max = await device.getMaxColors(); - console.log(`Found device ${device.name}: - Colors: - RED ${colors.red} - GREEN ${colors.green} - BLUE ${colors.blue} - Max colors: - RED ${max.red} - GREEN ${max.green} - BLUE ${max.blue}`) - const newColors = { - red: Math.floor(Math.random() * max.red), - green: Math.floor(Math.random() * max.green), - blue: Math.floor(Math.random() * max.blue), - }; - await device.setColors(newColors); - console.log(` - Updated colors: - RED ${newColors.red} - GREEN ${newColors.green} - BLUE ${newColors.blue}`); -} \ No newline at end of file diff --git a/joystick.ts b/joystick.ts index db11a96..d01c1b2 100644 --- a/joystick.ts +++ b/joystick.ts @@ -1,4 +1,4 @@ -import { exec, readUTF, writeUTF } from "./utils"; +import { exec, readUTF, writeUTF } from "./utils.js"; import * as fs from "fs"; interface IJoyStick { diff --git a/package.json b/package.json index b9af5bc..e804019 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,10 @@ { "dependencies": { + "@types/node": "^16.9.1", "got": "^11.8.2", - "node-vibrant": "^3.2.1-alpha.1" - } + "node-vibrant": "^3.2.1-alpha.1", + "ts-node": "^10.2.1", + "typescript": "^4.4.3" + }, + "type": "module" } diff --git a/spotify.ts b/spotify.ts deleted file mode 100644 index 63f5ae9..0000000 --- a/spotify.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { exec } from "./utils"; - -interface Track { - length: number; - artURL: string; - id: string; - url: string; - artist: string; - album: string; - albumArtist: string; -} - -class Spotify { - async getCurrentTrack() { - const data: Record = {}; - const out = await exec(["qdbus", "org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player.Metadata"]); - - } -} - -export default new Spotify(); \ No newline at end of file diff --git a/spotifyColors/index.ts b/spotifyColors/index.ts new file mode 100644 index 0000000..351c758 --- /dev/null +++ b/spotifyColors/index.ts @@ -0,0 +1,48 @@ +import { JoyStick } from "../joystick.js"; +import spotify from "./spotify.js"; +import { getColor, lerp } from "../utils.js"; + +type ThenArg = T extends PromiseLike ? U : T; +type Palette = ThenArg>; + +const devices = await JoyStick.getList(); +var color: Palette, lastURL: string, lastColor: Palette["DarkMuted"]; + +const perColor = 1000; + +setInterval(async () => { + const track = await spotify.getCurrentTrack(); + if(lastURL !== track.artURL) { + console.log("Track:", track.artist, "-", track.title); + color = await getColor(track.artURL); + lastURL = track.artURL; + } + + function transitionColors(startColor: Palette["DarkMuted"], endColor: Palette["DarkMuted"]) { + return new Promise((resolve) => { + const start = Date.now(); + var i = setInterval(async() => { + if(start + perColor < Date.now()) { + resolve(); + return clearInterval(i); + } + const progress = (Date.now() - start) / perColor; + + const newColors = { + red: Math.round(lerp(startColor.r, endColor.r, progress)), + green: Math.round(lerp(startColor.g, endColor.g, progress)), + blue: Math.round(lerp(startColor.b, endColor.b, progress)), + }; + + for(const device of devices) { + await device.setColors(newColors); + } + }, 10); + }); + } + + await transitionColors(lastColor || color.LightMuted, color.LightVibrant); + await transitionColors(color.LightVibrant, color.Vibrant); + await transitionColors(color.Vibrant, color.DarkVibrant); + lastColor = color.DarkVibrant; +}, perColor * 3); \ No newline at end of file diff --git a/spotifyColors/spotify.ts b/spotifyColors/spotify.ts new file mode 100644 index 0000000..711456f --- /dev/null +++ b/spotifyColors/spotify.ts @@ -0,0 +1,37 @@ +import { exec, getCurrentUser } from "../utils.js"; + +interface Track { + length: number; + title: string; + artURL: string; + id: string; + url: string; + artist: string; + album: string; + albumArtist: string; +} + +class Spotify { + async getCurrentTrack() { + const data: Record = {}; + const out = await exec(["machinectl", "shell", "--uid=" + await getCurrentUser(), ".host", "/usr/bin/qdbus", "org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player.Metadata"]); + for(const line of out.split("\n")) { + const key = line.substr(0, line.indexOf(" ")).trim(); + if(!key) continue; + const value = line.substr(line.indexOf(" ") + 1).trim(); + data[key] = value; + } + return { + length: Number(data["mpris:length:"]), + title: data["xesam:title:"], + artURL: data["mpris:artUrl:"], + id: data["mpris:trackid:"], + url: data["xesam:url:"], + artist: data["xesam:artist:"], + album: data["xesam:album:"], + albumArtist: data["xesam:albumArtist:"] + } as Track; + } +} + +export default new Spotify(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c159a4c..f5baf02 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,15 @@ { "compilerOptions": { "module": "esnext", + "allowJs": true, "target": "es2020", "moduleResolution": "node", - "allowSyntheticDefaultImports": true - } + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "outDir": "dist" + }, + "buildOptions": { + "incremental": true + }, + "ts-node": {} } \ No newline at end of file diff --git a/utils.ts b/utils.ts index 6764556..3695fe4 100644 --- a/utils.ts +++ b/utils.ts @@ -7,25 +7,37 @@ export async function readUTF(file: string) { return await afs.readFile(file, { encoding: "utf-8" }) as string; } export async function writeUTF(file: string, data: string) { - return await afs.writeFile(file, data); + await afs.writeFile(file, data); } export function exec(cmd: string[]) { return new Promise((resolve, reject) => { var output = ""; + var error = ""; const p = spawn(cmd.shift(), cmd); p.stdout.on("data", (chunk) => output += chunk); + p.stderr.on("data", (chunk) => error += chunk); + p.on("exit", (code) => { - if(code) return reject(code); + if(code) return reject(error); resolve(output); }); }); } +export async function getCurrentUser() { + return "dan"; +} + export async function getColor(image: string) { var resp = await got(image, { responseType: "buffer" }); var vibrant = new Vibrant(resp.body); var color = await vibrant.getPalette(); -} \ No newline at end of file + return color; +} + +export function lerp(a: number, b: number, u: number) { + return (1 - u) * a + u * b; +}; \ No newline at end of file