26
26
[Source](https://github.com/JnyJny/busylight.git)
27
27
"""
28
28
29
- # FastAPI Security Scheme
30
29
busylightapi_security = HTTPBasic ()
31
30
32
31
33
32
class BusylightAPI (FastAPI ):
34
33
def __init__ (self ):
35
- # Get and save the debug flag
36
34
debug = environ .get ("BUSYLIGHT_DEBUG" , False )
37
35
logger .info (f"Debug: { debug } " )
38
36
@@ -49,13 +47,11 @@ def __init__(self):
49
47
self .username = None
50
48
self .password = None
51
49
52
- # Get and save the CORS Access-Control-Allow-Origin header
53
50
logger .info (
54
51
"Set up CORS Access-Control-Allow-Origin header, if environment variable BUSYLIGHT_API_CORS_ORIGINS_LIST is set." ,
55
52
)
56
53
self .origins = json_loads (environ .get ("BUSYLIGHT_API_CORS_ORIGINS_LIST" , "[]" ))
57
54
58
- # Validate that BUSYLIGHT_API_CORS_ORIGINS_LIST is a list of strings
59
55
if (not isinstance (self .origins , list )) or any (
60
56
not isinstance (item , str ) for item in self .origins
61
57
):
@@ -90,7 +86,6 @@ def lights(self) -> list[Light]:
90
86
return self .controller .lights
91
87
92
88
def update (self ) -> None :
93
- # Force refresh of lights in controller
94
89
_ = self .controller .lights
95
90
96
91
def release (self ) -> None :
@@ -111,10 +106,6 @@ async def apply_effect(self, effect: Effects, light_id: int = None) -> None:
111
106
def get (self , path : str , ** kwargs ) -> Callable :
112
107
self .endpoints .append (path )
113
108
114
- # CORS allowed origins (for the Access-Control-Allow-Origin header)
115
- # are set through an environment variable BUSYLIGHT_API_CORS_ORIGINS_LIST
116
- # e.g.: export BUSYLIGHT_API_CORS_ORIGINS_LIST='["http://localhost", "http://localhost:8080"]'
117
- # (see https://fastapi.tiangolo.com/tutorial/cors/ for details)
118
109
if self .origins :
119
110
self .add_middleware (
120
111
CORSMiddleware ,
@@ -141,8 +132,6 @@ def authenticate_user(
141
132
busylightapi = BusylightAPI ()
142
133
143
134
144
- ## Startup & Shutdown
145
- ##
146
135
@busylightapi .on_event ("startup" )
147
136
async def startup () -> None :
148
137
busylightapi .update ()
@@ -157,8 +146,6 @@ async def shutdown() -> None:
157
146
logger .debug ("problem during shutdown: {error}" )
158
147
159
148
160
- ## Exception Handlers
161
- ##
162
149
@busylightapi .exception_handler (LightUnavailableError )
163
150
async def light_unavailable_handler (
164
151
request : Request ,
@@ -207,8 +194,6 @@ async def color_lookup_error_handler(
207
194
)
208
195
209
196
210
- ## Middleware Handlers
211
- ##
212
197
@busylightapi .middleware ("http" )
213
198
async def light_manager_update (request : Request , call_next : Callable ) -> Response :
214
199
"""Check for plug/unplug events and update the light manager."""
@@ -217,8 +202,6 @@ async def light_manager_update(request: Request, call_next: Callable) -> Respons
217
202
return await call_next (request )
218
203
219
204
220
- ## GET API Routes
221
- ##
222
205
@busylightapi .get ("/" , response_model = list [EndPoint ])
223
206
async def available_endpoints () -> list [dict [str , str ]]:
224
207
"""API endpoint listing.
@@ -303,6 +286,7 @@ async def light_on(
303
286
light_id : int = Path (..., title = "Numeric light identifier" , ge = 0 ),
304
287
color : str = "green" ,
305
288
dim : float = 1.0 ,
289
+ led : int = 0 ,
306
290
) -> dict [str , Any ]:
307
291
"""Turn on the specified light with the given `color`.
308
292
@@ -311,17 +295,22 @@ async def light_on(
311
295
312
296
`color` can be a color name or a hexadecimal string e.g. "red",
313
297
"#ff0000", "#f00", "0xff0000", "0xf00", "f00", "ff0000"
298
+
299
+ `led` parameter targets specific LEDs on multi-LED devices:
300
+ - 0 = all LEDs (default)
301
+ - 1+ = specific LED (1=first/top, 2=second/bottom, etc.)
314
302
"""
315
303
rgb = parse_color_string (color , dim )
316
- steady = Effects . for_name ( "steady" )( rgb )
317
- await busylightapi .apply_effect ( steady , light_id )
304
+
305
+ busylightapi .controller . by_index ( light_id ). turn_on ( rgb , led = led )
318
306
319
307
return {
320
308
"action" : "on" ,
321
309
"light_id" : light_id ,
322
310
"color" : color ,
323
311
"rgb" : rgb ,
324
312
"dim" : dim ,
313
+ "led" : led ,
325
314
}
326
315
327
316
@@ -332,22 +321,28 @@ async def light_on(
332
321
async def lights_on (
333
322
color : str = "green" ,
334
323
dim : float = 1.0 ,
324
+ led : int = 0 ,
335
325
) -> dict [str , Any ]:
336
326
"""Turn on all lights with the given `color`.
337
327
338
328
`color` can be a color name or a hexadecimal string e.g. "red",
339
329
"#ff0000", "#f00", "0xff0000", "0xf00", "f00", "ff0000"
330
+
331
+ `led` parameter targets specific LEDs on multi-LED devices:
332
+ - 0 = all LEDs (default)
333
+ - 1+ = specific LED (1=first/top, 2=second/bottom, etc.)
340
334
"""
341
335
rgb = parse_color_string (color , dim )
342
- steady = Effects . for_name ( "steady" )( rgb )
343
- await busylightapi .apply_effect ( steady )
336
+
337
+ busylightapi .controller . all (). turn_on ( rgb , led = led )
344
338
345
339
return {
346
340
"action" : "on" ,
347
341
"light_id" : "all" ,
348
342
"color" : color ,
349
343
"rgb" : rgb ,
350
344
"dim" : dim ,
345
+ "led" : led ,
351
346
}
352
347
353
348
@@ -397,6 +392,7 @@ async def blink_light(
397
392
speed : Speed = Speed .Slow ,
398
393
dim : float = 1.0 ,
399
394
count : int = 0 ,
395
+ led : int = 0 ,
400
396
) -> dict [str , Any ]:
401
397
"""Start blinking the specified light: color and off.
402
398
@@ -407,12 +403,15 @@ async def blink_light(
407
403
#ff0000, #f00, 0xff0000, 0xf00, f00, ff0000
408
404
409
405
`count` is the number of times to blink the light.
406
+
407
+ `led` parameter targets specific LEDs on multi-LED devices:
408
+ - 0 = all LEDs (default)
409
+ - 1+ = specific LED (1=first/top, 2=second/bottom, etc.)
410
410
"""
411
411
rgb = parse_color_string (color , dim )
412
412
413
- # Use controller's fluent API
414
413
selection = busylightapi .controller .by_index (light_id )
415
- selection .blink (rgb , count = count , speed = speed .name .lower ())
414
+ selection .blink (rgb , count = count , speed = speed .name .lower (), led = led )
416
415
417
416
return {
418
417
"action" : "blink" ,
@@ -422,6 +421,7 @@ async def blink_light(
422
421
"speed" : speed ,
423
422
"dim" : dim ,
424
423
"count" : count ,
424
+ "led" : led ,
425
425
}
426
426
427
427
@@ -434,15 +434,19 @@ async def blink_lights(
434
434
speed : Speed = Speed .Slow ,
435
435
dim : float = 1.0 ,
436
436
count : int = 0 ,
437
+ led : int = 0 ,
437
438
) -> dict [str , Any ]:
438
439
"""Start blinking all the lights: red and off
439
440
<p>Note: lights will not be synchronized.</p>
441
+
442
+ `led` parameter targets specific LEDs on multi-LED devices:
443
+ - 0 = all LEDs (default)
444
+ - 1+ = specific LED (1=first/top, 2=second/bottom, etc.)
440
445
"""
441
446
rgb = parse_color_string (color , dim )
442
447
443
- # Use controller's fluent API
444
448
selection = busylightapi .controller .all ()
445
- selection .blink (rgb , count = count , speed = speed .name .lower ())
449
+ selection .blink (rgb , count = count , speed = speed .name .lower (), led = led )
446
450
447
451
return {
448
452
"action" : "blink" ,
@@ -452,6 +456,7 @@ async def blink_lights(
452
456
"speed" : speed ,
453
457
"dim" : dim ,
454
458
"count" : count ,
459
+ "led" : led ,
455
460
}
456
461
457
462
0 commit comments