Compare commits

...
Sign in to create a new pull request.

16 commits

Author SHA1 Message Date
iGoX
77d4ea3019
Update README.md 2025-04-17 17:33:18 +02:00
iGoX
370eb78ba7
Update Web UI screenshot (#2) 2025-03-02 04:59:48 +01:00
iGoX
df196c4f6e
Update README.md 2025-03-02 04:39:21 +01:00
Nico
3a6e753fd4
Adding advanced Styling (#1) 2025-03-02 04:25:40 +01:00
iGoX
62c8ba4ad4 Update show off video (#33) 2025-01-16 14:27:05 +01:00
iGoX
c4aa9d9871 Show off video added (#32) 2025-01-16 13:58:07 +01:00
iGoX
cef62a3c4d Add link to Elgato marketplace (#31) 2025-01-10 13:02:34 +01:00
iGoX
995082abdf Update SDP v0.3.1 screenshot (#30) 2025-01-07 10:07:15 +01:00
iGoX
8a3124294e Update streamdeck-plugin/README.md (#29) 2025-01-07 09:57:33 +01:00
iGoX
91e1a6bfe4 Update "Set brightness" action (#28) 2025-01-07 09:54:36 +01:00
iGoX
9c27320ed8 Update README.md (#27) 2025-01-07 08:47:58 +01:00
iGoX
081ea28b82 Update README.md (#26) 2025-01-07 08:47:27 +01:00
iGoX
a8855f8e4e Update streamdeck-plugin/README.md (#25) 2025-01-07 00:42:26 +01:00
iGoX
67de2231bd Stream Deck plugin v0.3.1 (#24) 2025-01-06 23:44:06 +01:00
iGoX
b1bbabe90e Stream Deck plugin v0.3.0.0 (#23) 2025-01-06 20:26:19 +01:00
iGoX
3650a5213e Update README.md (#22) 2025-01-02 23:09:41 +01:00
50 changed files with 291 additions and 115 deletions

View file

@ -4,38 +4,194 @@
<title>BusyLight</title>
<script src="static/libs/busylight-api.js"></script>
<script src="static/libs/jscolor.min.js"></script>
<style>
/* General Reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: #ffffff;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
/* Glassmorphism Container */
section {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 30px;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
width: 90%;
max-width: 500px;
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
h1 {
font-size: 28px;
margin-bottom: 30px;
color: #ffffff;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
/* Radio Buttons */
div {
margin: 20px 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
transition: all 0.3s ease;
}
div:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
label {
font-size: 20px;
font-weight: 500;
margin-right: 15px;
cursor: pointer;
}
input[type="radio"] {
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
border: 3px solid #ffffff;
transition: all 0.3s ease;
cursor: pointer;
}
input[type="radio"]:checked {
background: #4CAF50;
border-color: #4CAF50;
box-shadow: 0 0 10px rgba(76, 175, 80, 0.7);
}
/* Color Picker Button */
#colorPicker {
background: linear-gradient(135deg, #007bff, #0056b3);
border: none;
padding: 15px 30px;
color: white;
font-size: 18px;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
}
#colorPicker:hover {
background: linear-gradient(135deg, #0056b3, #003d80);
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 123, 255, 0.4);
}
/* Brightness Slider */
input[type="range"] {
width: 100%;
cursor: pointer;
-webkit-appearance: none;
background: linear-gradient(135deg, #4CAF50, #45a049);
height: 10px;
border-radius: 5px;
outline: none;
transition: all 0.3s ease;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 25px;
height: 25px;
background: #ffffff;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
input[type="range"]:hover::-webkit-slider-thumb {
transform: scale(1.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
}
/* Responsive Design */
@media (max-width: 600px) {
section {
padding: 20px;
}
h1 {
font-size: 24px;
}
label {
font-size: 18px;
}
input[type="radio"] {
width: 22px;
height: 22px;
}
#colorPicker {
padding: 12px 24px;
font-size: 16px;
}
input[type="range"]::-webkit-slider-thumb {
width: 20px;
height: 20px;
}
}
</style>
</head>
<body onload="initForm()">
<h1>Status</h1>
<section>
<h1>BusyLight Status</h1>
<div>
<input type="radio" name="status" id="off" value="off" onclick="putStatus(this.value)" />
<label for="off">OFF</label>
</div>
<div>
<input type="radio" name="status" id="busy" value="busy" onclick="putStatus(this.value)" />
<label for="busy">Busy</label>
</div>
<div>
<input type="radio" name="status" id="available" value="available" onclick="putStatus(this.value)" />
<label for="available">Available</label>
</div>
<div>
<input type="radio" name="status" id="away" value="away" onclick="putStatus(this.value)" />
<label for="away">Away</label>
</div>
<div>
<input type="radio" name="status" id="colored" value="colored" onclick="openColorPicker()" />
<label for="colored">Colored: </label>
<label for="colored">Colored</label>
<button id="colorPicker">Pick color</button>
</div>
<div>
<input type="range" id="brightness" name="brightness" min="0" max="100" step="1" onchange="putBrightness(this.value)"/>
<label for="brightness">Brightness</label>
@ -48,7 +204,6 @@
alphaChannel: false
};
//var colorPicker = new JSColor("colorPicker", "{preset:'default', onChange: 'putColor(this)'}")
var colorPickerOpts = {};
colorPickerOpts["preset"] = "rgb";
colorPickerOpts["format"] = "rgb";
@ -102,7 +257,6 @@
statusRadio.checked = true;
}
async function putStatus(status) {
var s = await setStatus(status);
setColorPickerColor(status);
@ -131,6 +285,5 @@
var b = await setBrightness(brightness);
}
</script>
</body>
</html>

View file

@ -16,28 +16,33 @@
**Let people know if they can bother you with light signals!**
A simple to build, nice looking, portable DIY **Busy Light**.
A cheap, simple to build, nice looking and portable DIY **Busy Light**.
It comes with a with a rudimentary (and ugly) **Web UI** and a simple (but hopefully convenient) **Rest API**.
It comes with a with a simplistic but neat **Web UI** and a simple (but hopefully convenient) **Rest API**.
| Controlled by Stream Deck with REST API | Light roll |
|-------------------------------------------------|---------------------------------------|
| ![busylight and stream deck](img/busylight.jpg) | ![busylight roll](img/busylight.gif) |
![showoff video](img/busylight-showoff.mp4)
# Web UI
A very simplistic (and ugly) UI is available on port `80`. \
A very simplistic but neat UI is available on port `80` (thanks @nicolaeser). \
Default hostname is `igox-busylight`.
You can try to reach the web UI @ `http://igox-busylight.local`.
You can try to reach the web UI @ [http://igox-busylight.local](http://igox-busylight.local).
| What an ugly UI ! :smile: |
| What an neat UI ! :smile: |
|---------------------------|
| ![Web UI](img/web-ui.png) |
# Stream Deck plug-in
You can download a Stream Deck plugin to control your BusyLight [here](streamdeck-plugin/README.md).
You can download a Stream Deck plugin to control your BusyLight:
I will (hopefully) publish it to the [Elgato Market Place](https://marketplace.elgato.com/stream-deck/plugins) soon.
[![get it on marketplace](img/elgato-marketplace.png "Get iGoX BusyLight on Marketplace")](https://marketplace.elgato.com/product/igox-busylight-7448a0be-6dd6-4711-ba0d-86c52b9075b9)
Or directly from [here](streamdeck-plugin/README.md).
# BusyLight API
## End points

BIN
img/busylight-showoff.mp4 Normal file

Binary file not shown.

BIN
img/elgato-marketplace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Before After
Before After

View file

@ -1,4 +1,59 @@
# Release notes
# BusyLight Stream Deck plugin - Release notes
## Version 0.3.1.0 (2024-01-06)
### Download
[org.igox.busylight.v0.3.1.0.streamDeckPlugin](download/org.igox.busylight.v0.3.1.0.streamDeckPlugin)
### Features
- None added
### Enhancements
- Add colored button for "Set color" action.
- Add colored button for "Set brightness" action.
- Update plugin and plugin category icons
### Fixes
- None.
### Bugs & known limitations
- None known at publication time.
### Screenshot
![v0.3.1.0 screenshot](img/v0.3.1.0.png)
## Version 0.3.0.0 (2024-01-06)
### Download
[org.igox.busylight.v0.3.0.0.streamDeckPlugin](download/org.igox.busylight.v0.3.0.0.streamDeckPlugin)
### Features
- None added
### Enhancements
- Rework icons to comply with plugin guidelines for Elgato Marketplace
- Combine the 3 "status" actions into a single action and having the status be selected with a dropdown
### Fixes
- None.
### Bugs & known limitations
- None known at publication time.
### Screenshot
![v0.3.0.0 screenshot](img/v0.3.0.0.png)
## Version 0.2.0.0 (2024-12-28)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

View file

@ -1,69 +1,21 @@
{
"Name": "iGoX BusyLight",
"Version": "0.2.0.0",
"Version": "0.3.1.0",
"Author": "iGoX",
"$schema": "https://schemas.elgato.com/streamdeck/plugins/manifest.json",
"Actions": [
{
"Name": "Set status as 'Available'",
"UUID": "org.igox.busylight.status.available",
"Icon": "imgs/actions/status/available/available",
"Tooltip": "Set status as 'Available'",
"Name": "Set BusyLight status",
"UUID": "org.igox.busylight.status.set",
"Icon": "imgs/actions/icons/status/status",
"Tooltip": "Set BusyLight status",
"PropertyInspectorPath": "ui/status-config.html",
"Controllers": [
"Keypad"
],
"States": [
{
"Image": "imgs/actions/status/available/available",
"TitleAlignment": "bottom"
}
]
},
{
"Name": "Set status as 'Away'",
"UUID": "org.igox.busylight.status.away",
"Icon": "imgs/actions/status/away/away",
"Tooltip": "Set status as 'Away'",
"PropertyInspectorPath": "ui/status-config.html",
"Controllers": [
"Keypad"
],
"States": [
{
"Image": "imgs/actions/status/away/away",
"TitleAlignment": "bottom"
}
]
},
{
"Name": "Set status as 'Busy'",
"UUID": "org.igox.busylight.status.busy",
"Icon": "imgs/actions/status/busy/busy",
"Tooltip": "Set status as 'Busy'",
"PropertyInspectorPath": "ui/status-config.html",
"Controllers": [
"Keypad"
],
"States": [
{
"Image": "imgs/actions/status/busy/busy",
"TitleAlignment": "bottom"
}
]
},
{
"Name": "Turn off the BusyLight",
"UUID": "org.igox.busylight.status.off",
"Icon": "imgs/actions/status/off/off",
"Tooltip": "Turn off the BusyLight",
"PropertyInspectorPath": "ui/status-config.html",
"Controllers": [
"Keypad"
],
"States": [
{
"Image": "imgs/actions/status/off/off",
"Image": "imgs/actions/icons/status/status",
"TitleAlignment": "bottom"
}
]
@ -71,7 +23,7 @@
{
"Name": "Set brightness",
"UUID": "org.igox.busylight.brigthness.set",
"Icon": "imgs/actions/brightness/brightness",
"Icon": "imgs/actions/icons/brightness/brightness",
"Tooltip": "Set LED brightness",
"PropertyInspectorPath": "ui/brightness-config.html",
"Controllers": [
@ -79,7 +31,7 @@
],
"States": [
{
"Image": "imgs/actions/brightness/brightness",
"Image": "imgs/actions/icons/brightness/brightness",
"TitleAlignment": "bottom"
}
]
@ -87,7 +39,7 @@
{
"Name": "Set color",
"UUID": "org.igox.busylight.color.set",
"Icon": "imgs/actions/color/color",
"Icon": "imgs/actions/icons/color/color",
"Tooltip": "Set BusyLight displayed color",
"PropertyInspectorPath": "ui/color-config.html",
"Controllers": [
@ -95,17 +47,17 @@
],
"States": [
{
"Image": "imgs/actions/color/color",
"Image": "imgs/actions/icons/color/color",
"TitleAlignment": "bottom"
}
]
}
],
"Category": "iGoX BusyLight",
"CategoryIcon": "imgs/plugin/marketplace",
"CategoryIcon": "imgs/plugin/category-icon",
"CodePath": "bin/plugin.js",
"Description": "Control your DIY BusyLight (https://github.com/igox/busylight) from your Stream Deck",
"Icon": "imgs/plugin/marketplace",
"Icon": "imgs/plugin/icon",
"SDKVersion": 2,
"Software": {
"MinimumVersion": "6.4"

View file

@ -25,5 +25,14 @@
value="getGlobalSettings()">
</sdpi-textfield>
</sdpi-item>
<sdpi-item label="Status">
<sdpi-select setting="status" placeholder="Please choose a status">
<option value="available">Available</option>
<option value="away">Away</option>
<option value="busy">Busy</option>
<option value="on">On</option>
<option value="off">Off</option>
</sdpi-select>
</sdpi-item>
</body>
</html>

View file

@ -1,4 +1,4 @@
import streamDeck, { action, DidReceiveSettingsEvent, WillAppearEvent, KeyDownEvent, SingletonAction } from "@elgato/streamdeck";
import streamDeck, { action, DidReceiveSettingsEvent, WillAppearEvent, KeyDownEvent, PropertyInspectorDidAppearEvent, SingletonAction } from "@elgato/streamdeck";
@action({ UUID: "org.igox.busylight.brigthness.set" })
export class SetBrightness extends SingletonAction<BrightnessSettings> {
@ -25,7 +25,13 @@ export class SetBrightness extends SingletonAction<BrightnessSettings> {
const { settings } = ev.payload;
await ev.action.setSettings(settings);
await ev.action.setTitle(`${settings.brightness} %`);
await ev.action.setTitle(`${settings.brightness}%`);
}
override async onPropertyInspectorDidAppear(ev: PropertyInspectorDidAppearEvent<BrightnessSettings>): Promise<void> {
streamDeck.logger.debug(`>>> Received onPropertyInspectorDidAppear. Setting action icon <<<`);
await ev.action.setImage(`imgs/actions/buttons/brigthness/brigthness.png`);
}
}

View file

@ -1,4 +1,4 @@
import streamDeck, { action, JsonObject, KeyDownEvent, DidReceiveSettingsEvent, SingletonAction } from "@elgato/streamdeck";
import streamDeck, { action, JsonObject, KeyDownEvent, DidReceiveSettingsEvent, PropertyInspectorDidAppearEvent, SingletonAction } from "@elgato/streamdeck";
@action({ UUID: "org.igox.busylight.color.set" })
export class SetColor extends SingletonAction {
@ -18,6 +18,12 @@ export class SetColor extends SingletonAction {
const { settings } = ev.payload;
await ev.action.setSettings(settings);
}
override async onPropertyInspectorDidAppear(ev: PropertyInspectorDidAppearEvent<ColorSettings>): Promise<void> {
streamDeck.logger.debug(`>>> Color button property inspector diplayed! <<<`);
await ev.action.setImage(`imgs/actions/buttons/colored/colored.png`);
}
}
function hexToRgb(hex: string): { r: number, g: number, b: number } {

View file

@ -1,30 +1,20 @@
import streamDeck, { action, KeyDownEvent, SingletonAction } from "@elgato/streamdeck";
import streamDeck, { action, KeyDownEvent, SingletonAction, DidReceiveSettingsEvent } from "@elgato/streamdeck";
@action({ UUID: "org.igox.busylight.status.available" })
export class SetStatusAvailable extends SingletonAction {
override async onKeyDown(ev: KeyDownEvent): Promise<void> {
setStatus('available');
@action({ UUID: "org.igox.busylight.status.set" })
export class SetStatus extends SingletonAction {
override async onKeyDown(ev: KeyDownEvent<statusSettings>): Promise<void> {
const { settings } = ev.payload;
settings.status ??= 'available';
setStatus(settings.status);
}
}
@action({ UUID: "org.igox.busylight.status.busy" })
export class SetStatusBusy extends SingletonAction {
override async onKeyDown(ev: KeyDownEvent): Promise<void> {
setStatus('busy');
}
}
override async onDidReceiveSettings(ev: DidReceiveSettingsEvent<statusSettings>): Promise<void> {
const { settings } = ev.payload;
let status = settings.status;
streamDeck.logger.debug(`>>> Config status changed to: ${status} <<<`);
@action({ UUID: "org.igox.busylight.status.away" })
export class SetStatusAway extends SingletonAction {
override async onKeyDown(ev: KeyDownEvent): Promise<void> {
setStatus('away');
}
}
await ev.action.setImage(`imgs/actions/buttons/${status}/${status}.png`);
@action({ UUID: "org.igox.busylight.status.off" })
export class SetStatusOff extends SingletonAction {
override async onKeyDown(ev: KeyDownEvent): Promise<void> {
setStatus('off');
}
}
@ -44,3 +34,7 @@ async function setStatus(status: string) {
.then(response => response.json())
.then(data => streamDeck.logger.debug(data));
}
type statusSettings = {
status?: string;
};

View file

@ -1,7 +1,6 @@
import streamDeck, { LogLevel, SingletonAction, action, type DidReceiveSettingsEvent } from "@elgato/streamdeck";
//import { IncrementCounter } from "./actions/increment-counter";
import { SetStatusAvailable, SetStatusBusy, SetStatusAway, SetStatusOff } from "./actions/set-status";
import { SetStatus} from "./actions/set-status";
import { SetBrightness } from "./actions/set-brightness";
import { SetColor } from "./actions/set-color";
@ -9,10 +8,7 @@ import { SetColor } from "./actions/set-color";
streamDeck.logger.setLevel(LogLevel.INFO);
// Register the actions.
streamDeck.actions.registerAction(new SetStatusAvailable());
streamDeck.actions.registerAction(new SetStatusBusy());
streamDeck.actions.registerAction(new SetStatusAway());
streamDeck.actions.registerAction(new SetStatusOff());
streamDeck.actions.registerAction(new SetStatus());
streamDeck.actions.registerAction(new SetBrightness());
streamDeck.actions.registerAction(new SetColor());

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB