Compare commits

..

1 commit

Author SHA1 Message Date
iGoX
c0f1d9b79c
Blink feature - initial commit 2025-01-06 16:48:45 +01:00
51 changed files with 214 additions and 340 deletions

View file

@ -1,5 +1,6 @@
from microdot import Microdot, send_file from microdot import Microdot, send_file
import machine, sys, neopixel, time import machine, sys, neopixel, time
import asyncio
app = Microdot() app = Microdot()
@ -8,46 +9,65 @@ nbPixels = 12*2
pinPixelStrip = 16 # ESP32 D1 Mini pinPixelStrip = 16 # ESP32 D1 Mini
neoPixelStrip = neopixel.NeoPixel(machine.Pin(pinPixelStrip), nbPixels) neoPixelStrip = neopixel.NeoPixel(machine.Pin(pinPixelStrip), nbPixels)
# Define status colors # Define BusyLight status
statusColors = { statusDef = {
'BUSY': (255,0,0), # red 'off': {'name': 'off', 'code': 0, 'color': (0, 0, 0)}, # OFF
'AVAILABLE': (0,255,0), # green 'on': {'name': 'on', 'code': 1, 'color': (255, 255, 255)}, # White
'AWAY': (246,190,0), # cyan 'busy': {'name': 'busy', 'code': 2, 'color': (255, 0, 0)}, # Red
'OFF': (0, 0, 0), # off 'available': {'name': 'available', 'code': 3, 'color': (0, 255, 0)}, # Green
'ON': (255, 255, 255) # white 'away': {'name': 'away', 'code': 4, 'color': (246, 190, 0)}, # Yellow
} 'blinking': {'name': 'blinking', 'code': 5}, # No defined color
'colored': {'name': 'colored', 'code': 6} # No defined color
}
# Store BusyLight default global status # Store BusyLight default global status
blColor = statusColors.get('OFF') blColor = statusDef.get('off').get('color')
blStatus = 'off' blPreviousColor = statusDef.get('off').get('color')
blPreviousStatus='off' blStatus = statusDef.get('off')
blPreviousStatus = statusDef.get('off')
blBrightness = 0.1 # Adjust the brightness (0.0 - 1.0) blBrightness = 0.1 # Adjust the brightness (0.0 - 1.0)
blBlinkingTask = None
def __setColor(color): def __setDimmedColor(color):
r, g , b = color r, g , b = color
r = int(r * blBrightness) r = int(r * blBrightness)
g = int(g * blBrightness) g = int(g * blBrightness)
b = int(b * blBrightness) b = int(b * blBrightness)
return (r, g, b) return (r, g, b)
def __setBusyLightColor(color, brightness): def __setBusyLightColorAndBrigthness(color, brightness):
global blBrightness global blBrightness
blBrightness = brightness blBrightness = brightness
global blColor global blColor
blColor = color blColor = color
neoPixelStrip.fill(__setColor(color)) dimmedColor = __setDimmedColor(color)
neoPixelStrip.fill(dimmedColor)
neoPixelStrip.write() neoPixelStrip.write()
def __setBusyLightColored(color, brightness):
__setBusyLightColorAndBrigthness(color, brightness)
global blStatus global blStatus
blStatus = 'colored' blStatus = statusDef.get('colored')
def __setBusyLightStatus(status): def __setBusyLightStatus(status = statusDef.get('off')):
status = status.upper() if status == statusDef.get('colored'):
color = statusColors.get(status) lColor = blColor
__setBusyLightColor(color, blBrightness) else:
lColor = status.get('color')
__setBusyLightColorAndBrigthness(lColor, blBrightness)
global blStatus global blStatus
blStatus = status.lower() blStatus = status
def __setBusyLightBlinking(time_ms=500):
return 0
def __setBusyLightStill():
return 0
# Microdot APP routes # Microdot APP routes
@ -80,16 +100,8 @@ async def setBrightness(request):
else: else:
return {'error': 'wrong brigthness type (float)'}, 400 return {'error': 'wrong brigthness type (float)'}, 400
# Save blStatus
global blStatus
status = blStatus
# Apply new brightness to current color # Apply new brightness to current color
color = blColor __setBusyLightColorAndBrigthness(blColor, brightness)
__setBusyLightColor(color, brightness)
# Restore global status
blStatus = status
global blBrightness global blBrightness
blBrightness = brightness blBrightness = brightness
@ -121,32 +133,71 @@ async def setColor(request):
brightness = request.json.get("brightness") brightness = request.json.get("brightness")
if not brightness is None: if not bool(brightness is None):
if type(brightness) is float \ if type(brightness) is float \
or type(brightness) is int: or type(brightness) is int:
if brightness < 0 or brightness > 1: if brightness < 0 or brightness > 1:
return {'error': 'brightness out of bound (0.0 - 1.0)'}, 400 return {'error': 'brightness out of bound (0.0 - 1.0)'}, 400
else: else:
return {'error': 'wrong brightness type (float)'}, 400 return {'error': 'wrong brightness type (float)'}, 400
__setBusyLightColor(color, brightness) __setBusyLightColored(color, brightness)
__setBusyLightColor(color, blBrightness) __setBusyLightColored(color, blBrightness)
return {'status': blStatus} return {'status': blStatus}
@app.post('/api/blink')
async def setBlink(request):
time_ms = request.json.get("time_ms")
blinking = request.json.get("blinking")
if not bool(time_ms is None):
if type(time_ms) is int:
if time_ms < 100 or time_ms > 5000:
return {'error': 'parameter "time_ms" out of bound (100 - 5000)'}, 400
else:
return {'error': 'wrong parameter "time_ms" type (int)'}, 400
if bool(blinking is None):
return {'error': 'missing blinking parameter'}, 400
else:
if type(blinking) is bool:
if blinking:
if time_ms is None:
__setBusyLightBlinking()
else:
__setBusyLightBlinking(time_ms)
else:
__setBusyLightStill()
else:
return {'error': 'wrong blinking type (bool)'}, 400
return {'status': 'to be implemented'}
@app.route('/api/status/<status>', methods=['GET', 'POST']) @app.route('/api/status/<status>', methods=['GET', 'POST'])
async def setStatus(request, status): async def setStatus(request, status):
lStatus = status.lower() lStatus = status.lower()
if lStatus == 'on': if lStatus == 'on':
__setBusyLightStatus('ON') status = statusDef.get('on')
__setBusyLightStatus(status)
elif lStatus == 'off': elif lStatus == 'off':
__setBusyLightStatus('OFF') status = statusDef.get('off')
__setBusyLightStatus(status)
elif lStatus == 'available': elif lStatus == 'available':
__setBusyLightStatus('AVAILABLE') status = statusDef.get('available')
__setBusyLightStatus(status)
elif lStatus == 'away': elif lStatus == 'away':
__setBusyLightStatus('AWAY') status = statusDef.get('away')
__setBusyLightStatus(status)
elif lStatus == 'busy': elif lStatus == 'busy':
__setBusyLightStatus('BUSY') status = statusDef.get('busy')
__setBusyLightStatus(status)
else: else:
return {'error': 'unknown /api/status/' + lStatus + ' route'}, 404 return {'error': 'unknown /api/status/' + lStatus + ' route'}, 404
@ -183,11 +234,11 @@ async def mutedeckWebhook(request):
isVideoOn = False isVideoOn = False
if isMuted: if isMuted:
__setBusyLightStatus('away') __setBusyLightStatus(statusDef.get('away'))
else: else:
__setBusyLightStatus('busy') __setBusyLightStatus(statusDef.get('busy'))
else: else:
__setBusyLightStatus('available') __setBusyLightStatus(statusDef.get('available'))
return {'status': blStatus} return {'status': blStatus}
@ -199,18 +250,17 @@ async def shutdown(request):
# Startup effect # Startup effect
def startUpSeq(): def startUpSeq():
print('Start seq begins') print('Start seq is started')
__setBusyLightStatus('OFF') __setBusyLightStatus(statusDef.get('off'))
time.sleep_ms(100) time.sleep_ms(100)
__setBusyLightStatus('BUSY') __setBusyLightStatus(statusDef.get('busy'))
time.sleep_ms(200) time.sleep_ms(200)
__setBusyLightStatus('AWAY') __setBusyLightStatus(statusDef.get('away'))
time.sleep_ms(300) time.sleep_ms(300)
__setBusyLightStatus('AVAILABLE') __setBusyLightStatus(statusDef.get('available'))
time.sleep_ms(500) time.sleep_ms(500)
__setBusyLightStatus('OFF') __setBusyLightStatus(statusDef.get('off'))
print('Start seq is ended') print('Start seq is ended')
__setBusyLightColor(statusColors.get('OFF'), 0.1)
startUpSeq() startUpSeq()

View file

@ -4,194 +4,38 @@
<title>BusyLight</title> <title>BusyLight</title>
<script src="static/libs/busylight-api.js"></script> <script src="static/libs/busylight-api.js"></script>
<script src="static/libs/jscolor.min.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> </head>
<body onload="initForm()"> <body onload="initForm()">
<h1>Status</h1>
<section> <section>
<h1>BusyLight Status</h1>
<div> <div>
<input type="radio" name="status" id="off" value="off" onclick="putStatus(this.value)" /> <input type="radio" name="status" id="off" value="off" onclick="putStatus(this.value)" />
<label for="off">OFF</label> <label for="off">OFF</label>
</div> </div>
<div> <div>
<input type="radio" name="status" id="busy" value="busy" onclick="putStatus(this.value)" /> <input type="radio" name="status" id="busy" value="busy" onclick="putStatus(this.value)" />
<label for="busy">Busy</label> <label for="busy">Busy</label>
</div> </div>
<div> <div>
<input type="radio" name="status" id="available" value="available" onclick="putStatus(this.value)" /> <input type="radio" name="status" id="available" value="available" onclick="putStatus(this.value)" />
<label for="available">Available</label> <label for="available">Available</label>
</div> </div>
<div> <div>
<input type="radio" name="status" id="away" value="away" onclick="putStatus(this.value)" /> <input type="radio" name="status" id="away" value="away" onclick="putStatus(this.value)" />
<label for="away">Away</label> <label for="away">Away</label>
</div> </div>
<div> <div>
<input type="radio" name="status" id="colored" value="colored" onclick="openColorPicker()" /> <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> <button id="colorPicker">Pick color</button>
</div> </div>
<div> <div>
<input type="range" id="brightness" name="brightness" min="0" max="100" step="1" onchange="putBrightness(this.value)"/> <input type="range" id="brightness" name="brightness" min="0" max="100" step="1" onchange="putBrightness(this.value)"/>
<label for="brightness">Brightness</label> <label for="brightness">Brightness</label>
@ -204,6 +48,7 @@
alphaChannel: false alphaChannel: false
}; };
//var colorPicker = new JSColor("colorPicker", "{preset:'default', onChange: 'putColor(this)'}")
var colorPickerOpts = {}; var colorPickerOpts = {};
colorPickerOpts["preset"] = "rgb"; colorPickerOpts["preset"] = "rgb";
colorPickerOpts["format"] = "rgb"; colorPickerOpts["format"] = "rgb";
@ -257,6 +102,7 @@
statusRadio.checked = true; statusRadio.checked = true;
} }
async function putStatus(status) { async function putStatus(status) {
var s = await setStatus(status); var s = await setStatus(status);
setColorPickerColor(status); setColorPickerColor(status);
@ -285,5 +131,6 @@
var b = await setBrightness(brightness); var b = await setBrightness(brightness);
} }
</script> </script>
</body> </body>
</html> </html>

View file

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

@ -1,59 +1,4 @@
# BusyLight Stream Deck plugin - Release notes # 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) ## Version 0.2.0.0 (2024-12-28)

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View file

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

View file

@ -25,14 +25,5 @@
value="getGlobalSettings()"> value="getGlobalSettings()">
</sdpi-textfield> </sdpi-textfield>
</sdpi-item> </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> </body>
</html> </html>

View file

@ -1,4 +1,4 @@
import streamDeck, { action, DidReceiveSettingsEvent, WillAppearEvent, KeyDownEvent, PropertyInspectorDidAppearEvent, SingletonAction } from "@elgato/streamdeck"; import streamDeck, { action, DidReceiveSettingsEvent, WillAppearEvent, KeyDownEvent, SingletonAction } from "@elgato/streamdeck";
@action({ UUID: "org.igox.busylight.brigthness.set" }) @action({ UUID: "org.igox.busylight.brigthness.set" })
export class SetBrightness extends SingletonAction<BrightnessSettings> { export class SetBrightness extends SingletonAction<BrightnessSettings> {
@ -25,13 +25,7 @@ export class SetBrightness extends SingletonAction<BrightnessSettings> {
const { settings } = ev.payload; const { settings } = ev.payload;
await ev.action.setSettings(settings); 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, PropertyInspectorDidAppearEvent, SingletonAction } from "@elgato/streamdeck"; import streamDeck, { action, JsonObject, KeyDownEvent, DidReceiveSettingsEvent, SingletonAction } from "@elgato/streamdeck";
@action({ UUID: "org.igox.busylight.color.set" }) @action({ UUID: "org.igox.busylight.color.set" })
export class SetColor extends SingletonAction { export class SetColor extends SingletonAction {
@ -18,12 +18,6 @@ export class SetColor extends SingletonAction {
const { settings } = ev.payload; const { settings } = ev.payload;
await ev.action.setSettings(settings); 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 } { function hexToRgb(hex: string): { r: number, g: number, b: number } {

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB