From b28c4d9d93b6c26d1ad6d5c038539e729aa9d3ae Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Mar 2023 13:06:02 +0000 Subject: [PATCH 1/5] Move current command from `src/index.tsx` to `src/manage-devices.tsx` --- package.json | 2 +- src/{index.tsx => manage-devices.tsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{index.tsx => manage-devices.tsx} (100%) diff --git a/package.json b/package.json index 38e6d5d..380b77d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "license": "MIT", "commands": [ { - "name": "index", + "name": "manage-devices", "title": "Manage Devices", "description": "Turn your Logitech Litra devices on or off", "mode": "view" diff --git a/src/index.tsx b/src/manage-devices.tsx similarity index 100% rename from src/index.tsx rename to src/manage-devices.tsx From 10b3efd100719d6bda9a15691325b8faf2f81579 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Mar 2023 14:19:49 +0000 Subject: [PATCH 2/5] Allow managing Litra devices' temperature and brightness using presets --- package.json | 17 +++++++- src/brightness-presets.ts | 33 ++++++++++++++ src/manage-brightness-presets.tsx | 70 ++++++++++++++++++++++++++++++ src/manage-devices.tsx | 51 +++++++++++++++++++++- src/manage-temperature-presets.tsx | 70 ++++++++++++++++++++++++++++++ src/temperature-presets.ts | 37 ++++++++++++++++ src/utils.ts | 16 +++++++ 7 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 src/brightness-presets.ts create mode 100644 src/manage-brightness-presets.tsx create mode 100644 src/manage-temperature-presets.tsx create mode 100644 src/temperature-presets.ts diff --git a/package.json b/package.json index 380b77d..a847cec 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,22 @@ { "name": "manage-devices", "title": "Manage Devices", - "description": "Turn your Logitech Litra devices on or off", + "subtitle": "Turn on and off, and set brightness and temperature", + "description": "Turn your Logitech Litra devices on or off, and set their brightness and temperature", + "mode": "view" + }, + { + "name": "manage-temperature-presets", + "title": "Manage Temperature Presets", + "subtitle": "Set up presets for your Litra devices' temperature", + "description": "Configure presets so you can manage your Litra devices' temperature from Raycast", + "mode": "view" + }, + { + "name": "manage-brightness-presets", + "title": "Manage Brightness Presets", + "subtitle": "Set up presets for your Litra devices' brightness", + "description": "Configure presets so you can manage your Litra devices' brightness from Raycast", "mode": "view" } ], diff --git a/src/brightness-presets.ts b/src/brightness-presets.ts new file mode 100644 index 0000000..d00caaf --- /dev/null +++ b/src/brightness-presets.ts @@ -0,0 +1,33 @@ +import { LocalStorage } from "@raycast/api"; + +const getEnabledBrightnessPresetsAsJson = async (): Promise => { + const enabledBrightnessPresetsAsJson = await LocalStorage.getItem("enabledBrightnessPresets"); + + if (typeof enabledBrightnessPresetsAsJson === "string") { + return enabledBrightnessPresetsAsJson; + } else { + return "[]"; + } +}; + +export const getEnabledBrightnessPresets = async (): Promise> => { + const enabledBrightnessPresetsAsJson = await getEnabledBrightnessPresetsAsJson(); + return new Set(JSON.parse(enabledBrightnessPresetsAsJson)); +}; + +const setEnabledBrightnessPresets = async (enabledBrightnessPresets: Set): Promise => { + await LocalStorage.setItem("enabledBrightnessPresets", JSON.stringify(Array.from(enabledBrightnessPresets))); +}; + +export const enableBrightnessPreset = async (brightnessPreset: number): Promise => { + const enabledBrightnessPresets = await getEnabledBrightnessPresets(); + setEnabledBrightnessPresets(enabledBrightnessPresets.add(brightnessPreset)); +}; + +export const disableBrightnessPreset = async (brightnessPreset: number): Promise => { + const enabledBrightnessPresets = await getEnabledBrightnessPresets(); + enabledBrightnessPresets.delete(brightnessPreset); + setEnabledBrightnessPresets(enabledBrightnessPresets); +}; + +export const SUPPORTED_BRIGHTNESS_PRESETS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; diff --git a/src/manage-brightness-presets.tsx b/src/manage-brightness-presets.tsx new file mode 100644 index 0000000..59f33dc --- /dev/null +++ b/src/manage-brightness-presets.tsx @@ -0,0 +1,70 @@ +import { ActionPanel, Action, Icon, List } from "@raycast/api"; +import React, { useEffect, useState } from "react"; +import { + disableBrightnessPreset, + enableBrightnessPreset, + getEnabledBrightnessPresets, + SUPPORTED_BRIGHTNESS_PRESETS, +} from "./brightness-presets"; + +interface BrightnessPresetOption { + value: number; + isEnabled: boolean; +} + +export default function Command() { + const [isLoading, setIsLoading] = useState(true); + const [brightnessPresets, setBrightnessPresets] = useState([]); + + const refreshBrightnessPresets = async (): Promise => { + const enabledBrightnessPresets = await getEnabledBrightnessPresets(); + const brightnessPresets = SUPPORTED_BRIGHTNESS_PRESETS.map((brightnessPreset) => ({ + value: brightnessPreset, + isEnabled: enabledBrightnessPresets.has(brightnessPreset), + })); + setBrightnessPresets(brightnessPresets); + setIsLoading(false); + }; + + useEffect(() => { + (async () => { + refreshBrightnessPresets(); + })(); + }, []); + + return ( + + {brightnessPresets.map((brightnessPreset) => ( + + {brightnessPreset.isEnabled && ( + { + await disableBrightnessPreset(brightnessPreset.value); + await refreshBrightnessPresets(); + }} + /> + )} + {!brightnessPreset.isEnabled && ( + { + await enableBrightnessPreset(brightnessPreset.value); + await refreshBrightnessPresets(); + }} + /> + )} + + } + /> + ))} + + ); +} diff --git a/src/manage-devices.tsx b/src/manage-devices.tsx index 850b79e..8b72095 100644 --- a/src/manage-devices.tsx +++ b/src/manage-devices.tsx @@ -1,8 +1,9 @@ import { ActionPanel, Action, Icon, List, showToast, Toast } from "@raycast/api"; import React, { useEffect, useState } from "react"; - import { getCliDirectory } from "./preferences"; -import { getDevices, turnOff, turnOn } from "./utils"; +import { getDevices, turnOff, turnOn, setTemperatureInKelvin, setBrightnessPercentage } from "./utils"; +import { getEnabledTemperaturePresets } from "./temperature-presets"; +import { getEnabledBrightnessPresets } from "./brightness-presets"; interface Device { name: string; @@ -11,6 +12,8 @@ interface Device { export default function Command() { const [devices, setDevices] = useState([]); + const [enabledTemperaturePresets, setEnabledTemperaturePresets] = useState>(new Set()); + const [enabledBrightnessPresets, setEnabledBrightnessPresets] = useState>(new Set()); const cliDirectory = getCliDirectory(); @@ -21,6 +24,22 @@ export default function Command() { })(); }, []); + useEffect(() => { + (async () => { + const enabledTemperaturePresets = await getEnabledTemperaturePresets(); + setEnabledTemperaturePresets(enabledTemperaturePresets); + })(); + }, []); + + useEffect(() => { + (async () => { + const enabledBrightnessPresets = await getEnabledBrightnessPresets(); + setEnabledBrightnessPresets(enabledBrightnessPresets); + })(); + }, []); + + console.log({ enabledTemperaturePresets, enabledBrightnessPresets }); + return ( {devices.map((device) => ( @@ -46,6 +65,34 @@ export default function Command() { await showToast({ title: `Turned off ${device.name}`, style: Toast.Style.Success }); }} /> + {Array.from(enabledTemperaturePresets).map((temperature) => ( + { + await setTemperatureInKelvin(cliDirectory, device.serial_number, temperature); + await showToast({ + title: `Set ${device.name}'s temperature to ${temperature}K`, + style: Toast.Style.Success, + }); + }} + /> + ))} + {Array.from(enabledBrightnessPresets).map((brightness) => ( + { + await setBrightnessPercentage(cliDirectory, device.serial_number, brightness); + await showToast({ + title: `Set ${device.name}'s brightness to ${brightness}%`, + style: Toast.Style.Success, + }); + }} + /> + ))} } /> diff --git a/src/manage-temperature-presets.tsx b/src/manage-temperature-presets.tsx new file mode 100644 index 0000000..dde43c0 --- /dev/null +++ b/src/manage-temperature-presets.tsx @@ -0,0 +1,70 @@ +import { ActionPanel, Action, Icon, List } from "@raycast/api"; +import React, { useEffect, useState } from "react"; +import { + disableTemperaturePreset, + enableTemperaturePreset, + getEnabledTemperaturePresets, + SUPPORTED_TEMPERATURE_PRESETS, +} from "./temperature-presets"; + +interface TemperaturePresetOption { + value: number; + isEnabled: boolean; +} + +export default function Command() { + const [isLoading, setIsLoading] = useState(true); + const [temperaturePresets, setTemperaturePresets] = useState([]); + + const refreshTemperaturePresets = async (): Promise => { + const enabledTemperaturePresets = await getEnabledTemperaturePresets(); + const temperaturePresets = SUPPORTED_TEMPERATURE_PRESETS.map((temperaturePreset) => ({ + value: temperaturePreset, + isEnabled: enabledTemperaturePresets.has(temperaturePreset), + })); + setTemperaturePresets(temperaturePresets); + setIsLoading(false); + }; + + useEffect(() => { + (async () => { + refreshTemperaturePresets(); + })(); + }, []); + + return ( + + {temperaturePresets.map((temperaturePreset) => ( + + {temperaturePreset.isEnabled && ( + { + await disableTemperaturePreset(temperaturePreset.value); + await refreshTemperaturePresets(); + }} + /> + )} + {!temperaturePreset.isEnabled && ( + { + await enableTemperaturePreset(temperaturePreset.value); + await refreshTemperaturePresets(); + }} + /> + )} + + } + /> + ))} + + ); +} diff --git a/src/temperature-presets.ts b/src/temperature-presets.ts new file mode 100644 index 0000000..2df1636 --- /dev/null +++ b/src/temperature-presets.ts @@ -0,0 +1,37 @@ +import { LocalStorage } from "@raycast/api"; + +const getEnabledTemperaturePresetsAsJson = async (): Promise => { + const enabledTemperaturePresetsAsJson = await LocalStorage.getItem("enabledTemperaturePresets"); + + if (typeof enabledTemperaturePresetsAsJson === "string") { + return enabledTemperaturePresetsAsJson; + } else { + return "[]"; + } +}; + +export const getEnabledTemperaturePresets = async (): Promise> => { + const enabledTemperaturePresetsAsJson = await getEnabledTemperaturePresetsAsJson(); + return new Set(JSON.parse(enabledTemperaturePresetsAsJson)); +}; + +const setEnabledTemperaturePresets = async (enabledTemperaturePresets: Set): Promise => { + await LocalStorage.setItem("enabledTemperaturePresets", JSON.stringify(Array.from(enabledTemperaturePresets))); +}; + +export const enableTemperaturePreset = async (temperaturePreset: number): Promise => { + const enabledTemperaturePresets = await getEnabledTemperaturePresets(); + setEnabledTemperaturePresets(enabledTemperaturePresets.add(temperaturePreset)); +}; + +export const disableTemperaturePreset = async (temperaturePreset: number): Promise => { + const enabledTemperaturePresets = await getEnabledTemperaturePresets(); + enabledTemperaturePresets.delete(temperaturePreset); + setEnabledTemperaturePresets(enabledTemperaturePresets); +}; + +export const SUPPORTED_TEMPERATURE_PRESETS = [ + 2700, 2800, 2900, 3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900, 4000, 4100, 4200, 4300, 4400, 4500, + 4600, 4700, 4800, 4900, 5000, 5100, 5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900, 6000, 6100, 6200, 6300, 6400, + 6500, +]; diff --git a/src/utils.ts b/src/utils.ts index 4aadbe8..1d23008 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -38,3 +38,19 @@ export const turnOn = async (cliDirectory: string, serialNumber: string): Promis export const turnOff = async (cliDirectory: string, serialNumber: string): Promise => { await runLitraCommand(cliDirectory, "litra-off", `--serial-number ${serialNumber}`); }; + +export const setTemperatureInKelvin = async ( + cliDirectory: string, + serialNumber: string, + temperatureInKelvin: number +): Promise => { + await runLitraCommand(cliDirectory, "litra-temperature-k", `${temperatureInKelvin} --serial-number ${serialNumber}`); +}; + +export const setBrightnessPercentage = async ( + cliDirectory: string, + serialNumber: string, + brightnessPercentage: number +): Promise => { + await runLitraCommand(cliDirectory, "litra-brightness", `${brightnessPercentage} --serial-number ${serialNumber}`); +}; From 087b004e81e4a91ca1b26ac8810f74e2cc294794 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Mar 2023 14:31:02 +0000 Subject: [PATCH 3/5] Mention brightness and temperature control in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b44a44..ccb490e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Logitech Litra extension for Raycast -This [Raycast](https://www.raycast.com/) extension allows you to turn your Logitech Litra Glow and/or Logitech Litra Beam light(s) on and off from Raycast. +This [Raycast](https://www.raycast.com/) extension allows you to manage your Logitech Litra Glow and/or Logitech Litra Beam light(s) from Raycast, turning them on and off and setting their brightness and temperature. ![Screenshot](screenshot.png?raw=true) From 3cc1095de47e12383ae72cc04ab26e25189b2b69 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Mar 2023 14:31:12 +0000 Subject: [PATCH 4/5] Improve documentation on litra npm package requirements --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccb490e..c03d5b0 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,6 @@ This [Raycast](https://www.raycast.com/) extension allows you to manage your Log ## Installation -To use this extension, as well as downloading the extension from the Raycast Store, you must also set up Node.js and npm, and then install the `litra` npm package by running `npm install -g litra`. +To use this extension, as well as downloading the extension from the Raycast Store, you must also set up [Node.js](https://nodejs.org/en/) and [`npm`](https://www.npmjs.com/), and then install the `litra` npm package globally by running `npm install -g litra`. -When you run the extension for the first time, you'll be prompted to provide the directory where the Litra CLI is installed. You can get this by running `dirname $(which litra-on)` from a terminal. \ No newline at end of file +When you run the extension for the first time, you'll be prompted to provide the directory where the `litra` package's CLI is installed. You can get this by running `dirname $(which litra-on)` from a terminal. From bd5c95197e9ccb11ec61e692bb239b1a09a7ca85 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Sat, 4 Mar 2023 14:35:35 +0000 Subject: [PATCH 5/5] Update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950053b..daf55bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ # Logitech Litra Changelog -## [Initial Version] - 2023-02-11 \ No newline at end of file +## [Brightness and temperature support] - 2023-03-04 + +- Add support for setting the brightness and temperature of your Litra devices - thanks to [@zalewskigrzegorz](https://github.com/zalewskigrzegorz) for the [suggestion](https://github.com/raycast/extensions/issues/5101)! + +## [Initial Version] - 2023-02-11