-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from timrogers/temperature-and-brightness
Allow managing Litra devices' temperature and brightness using presets
- Loading branch information
Showing
10 changed files
with
353 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
# Logitech Litra Changelog | ||
|
||
## [Initial Version] - 2023-02-11 | ||
## [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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
# 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) | ||
|
||
## 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. | ||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { LocalStorage } from "@raycast/api"; | ||
|
||
const getEnabledBrightnessPresetsAsJson = async (): Promise<string> => { | ||
const enabledBrightnessPresetsAsJson = await LocalStorage.getItem<string>("enabledBrightnessPresets"); | ||
|
||
if (typeof enabledBrightnessPresetsAsJson === "string") { | ||
return enabledBrightnessPresetsAsJson; | ||
} else { | ||
return "[]"; | ||
} | ||
}; | ||
|
||
export const getEnabledBrightnessPresets = async (): Promise<Set<number>> => { | ||
const enabledBrightnessPresetsAsJson = await getEnabledBrightnessPresetsAsJson(); | ||
return new Set(JSON.parse(enabledBrightnessPresetsAsJson)); | ||
}; | ||
|
||
const setEnabledBrightnessPresets = async (enabledBrightnessPresets: Set<number>): Promise<void> => { | ||
await LocalStorage.setItem("enabledBrightnessPresets", JSON.stringify(Array.from(enabledBrightnessPresets))); | ||
}; | ||
|
||
export const enableBrightnessPreset = async (brightnessPreset: number): Promise<void> => { | ||
const enabledBrightnessPresets = await getEnabledBrightnessPresets(); | ||
setEnabledBrightnessPresets(enabledBrightnessPresets.add(brightnessPreset)); | ||
}; | ||
|
||
export const disableBrightnessPreset = async (brightnessPreset: number): Promise<void> => { | ||
const enabledBrightnessPresets = await getEnabledBrightnessPresets(); | ||
enabledBrightnessPresets.delete(brightnessPreset); | ||
setEnabledBrightnessPresets(enabledBrightnessPresets); | ||
}; | ||
|
||
export const SUPPORTED_BRIGHTNESS_PRESETS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<boolean>(true); | ||
const [brightnessPresets, setBrightnessPresets] = useState<BrightnessPresetOption[]>([]); | ||
|
||
const refreshBrightnessPresets = async (): Promise<void> => { | ||
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 ( | ||
<List isLoading={isLoading}> | ||
{brightnessPresets.map((brightnessPreset) => ( | ||
<List.Item | ||
key={brightnessPreset.value} | ||
title={`${brightnessPreset.value}%`} | ||
icon={brightnessPreset.isEnabled ? Icon.Checkmark : Icon.EyeDisabled} | ||
actions={ | ||
<ActionPanel> | ||
{brightnessPreset.isEnabled && ( | ||
<Action | ||
title="Disable" | ||
icon={Icon.EyeDisabled} | ||
onAction={async () => { | ||
await disableBrightnessPreset(brightnessPreset.value); | ||
await refreshBrightnessPresets(); | ||
}} | ||
/> | ||
)} | ||
{!brightnessPreset.isEnabled && ( | ||
<Action | ||
title="Enable" | ||
icon={Icon.Checkmark} | ||
onAction={async () => { | ||
await enableBrightnessPreset(brightnessPreset.value); | ||
await refreshBrightnessPresets(); | ||
}} | ||
/> | ||
)} | ||
</ActionPanel> | ||
} | ||
/> | ||
))} | ||
</List> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { ActionPanel, Action, Icon, List, showToast, Toast } from "@raycast/api"; | ||
import React, { useEffect, useState } from "react"; | ||
import { getCliDirectory } from "./preferences"; | ||
import { getDevices, turnOff, turnOn, setTemperatureInKelvin, setBrightnessPercentage } from "./utils"; | ||
import { getEnabledTemperaturePresets } from "./temperature-presets"; | ||
import { getEnabledBrightnessPresets } from "./brightness-presets"; | ||
|
||
interface Device { | ||
name: string; | ||
serial_number: string; | ||
} | ||
|
||
export default function Command() { | ||
const [devices, setDevices] = useState<Device[]>([]); | ||
const [enabledTemperaturePresets, setEnabledTemperaturePresets] = useState<Set<number>>(new Set()); | ||
const [enabledBrightnessPresets, setEnabledBrightnessPresets] = useState<Set<number>>(new Set()); | ||
|
||
const cliDirectory = getCliDirectory(); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
const devices = await getDevices(cliDirectory); | ||
setDevices(devices); | ||
})(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
const enabledTemperaturePresets = await getEnabledTemperaturePresets(); | ||
setEnabledTemperaturePresets(enabledTemperaturePresets); | ||
})(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
const enabledBrightnessPresets = await getEnabledBrightnessPresets(); | ||
setEnabledBrightnessPresets(enabledBrightnessPresets); | ||
})(); | ||
}, []); | ||
|
||
console.log({ enabledTemperaturePresets, enabledBrightnessPresets }); | ||
|
||
return ( | ||
<List isLoading={false}> | ||
{devices.map((device) => ( | ||
<List.Item | ||
key={device.serial_number} | ||
title={device.name} | ||
subtitle={device.serial_number} | ||
actions={ | ||
<ActionPanel> | ||
<Action | ||
title="Turn On" | ||
icon={Icon.LightBulb} | ||
onAction={async () => { | ||
await turnOn(cliDirectory, device.serial_number); | ||
await showToast({ title: `Turned on ${device.name}`, style: Toast.Style.Success }); | ||
}} | ||
/> | ||
<Action | ||
title="Turn Off" | ||
icon={Icon.LightBulbOff} | ||
onAction={async () => { | ||
await turnOff(cliDirectory, device.serial_number); | ||
await showToast({ title: `Turned off ${device.name}`, style: Toast.Style.Success }); | ||
}} | ||
/> | ||
{Array.from(enabledTemperaturePresets).map((temperature) => ( | ||
<Action | ||
key={temperature} | ||
title={`Set Temperature to ${temperature}K`} | ||
icon={Icon.Temperature} | ||
onAction={async () => { | ||
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) => ( | ||
<Action | ||
key={brightness} | ||
title={`Set Brightness to ${brightness}%`} | ||
icon={Icon.CircleProgress100} | ||
onAction={async () => { | ||
await setBrightnessPercentage(cliDirectory, device.serial_number, brightness); | ||
await showToast({ | ||
title: `Set ${device.name}'s brightness to ${brightness}%`, | ||
style: Toast.Style.Success, | ||
}); | ||
}} | ||
/> | ||
))} | ||
</ActionPanel> | ||
} | ||
/> | ||
))} | ||
</List> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<boolean>(true); | ||
const [temperaturePresets, setTemperaturePresets] = useState<TemperaturePresetOption[]>([]); | ||
|
||
const refreshTemperaturePresets = async (): Promise<void> => { | ||
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 ( | ||
<List isLoading={isLoading}> | ||
{temperaturePresets.map((temperaturePreset) => ( | ||
<List.Item | ||
key={temperaturePreset.value} | ||
title={`${temperaturePreset.value}K`} | ||
icon={temperaturePreset.isEnabled ? Icon.Checkmark : Icon.EyeDisabled} | ||
actions={ | ||
<ActionPanel> | ||
{temperaturePreset.isEnabled && ( | ||
<Action | ||
title="Disable" | ||
icon={Icon.EyeDisabled} | ||
onAction={async () => { | ||
await disableTemperaturePreset(temperaturePreset.value); | ||
await refreshTemperaturePresets(); | ||
}} | ||
/> | ||
)} | ||
{!temperaturePreset.isEnabled && ( | ||
<Action | ||
title="Enable" | ||
icon={Icon.Checkmark} | ||
onAction={async () => { | ||
await enableTemperaturePreset(temperaturePreset.value); | ||
await refreshTemperaturePresets(); | ||
}} | ||
/> | ||
)} | ||
</ActionPanel> | ||
} | ||
/> | ||
))} | ||
</List> | ||
); | ||
} |
Oops, something went wrong.