Skip to content

Commit

Permalink
Merge pull request #15 from timrogers/temperature-and-brightness
Browse files Browse the repository at this point in the history
Allow managing Litra devices' temperature and brightness using presets
  • Loading branch information
timrogers authored Mar 4, 2023
2 parents e9cf4af + bd5c951 commit 4649f24
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 61 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
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
6 changes: 3 additions & 3 deletions README.md
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.
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,24 @@
"license": "MIT",
"commands": [
{
"name": "index",
"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"
}
],
Expand Down
33 changes: 33 additions & 0 deletions src/brightness-presets.ts
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];
55 changes: 0 additions & 55 deletions src/index.tsx

This file was deleted.

70 changes: 70 additions & 0 deletions src/manage-brightness-presets.tsx
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>
);
}
102 changes: 102 additions & 0 deletions src/manage-devices.tsx
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>
);
}
70 changes: 70 additions & 0 deletions src/manage-temperature-presets.tsx
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>
);
}
Loading

0 comments on commit 4649f24

Please sign in to comment.