You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+
## Project Overview
6
+
7
+
Status-Light monitors user status across collaboration platforms (Webex, Slack) and calendars (Office 365, Google Calendar) to display the "busiest" status on a Tuya RGB LED smart bulb. The application runs as a continuous polling loop inside a Docker container.
8
+
9
+
## Running the Application
10
+
11
+
**Local execution:**
12
+
```bash
13
+
python -u status-light/status-light.py
14
+
```
15
+
16
+
**Docker (preferred):**
17
+
```bash
18
+
docker run -e SOURCES=webex,office365 -e WEBEX_PERSONID=... portableprogrammer/status-light
If specificed, requires at least one of the available options. This will control which services Status-Light uses to determine overall availability status.
-`virtual` - Virtual light that logs status changes (for testing)
145
+
- Default value: `tuya`
146
+
147
+
Specifies the output target for status display. Use `virtual` for testing without hardware.
148
+
149
+
---
150
+
134
151
### **Statuses**
135
152
136
153
-*Optional*
@@ -155,6 +172,10 @@ If specificed, requires at least one of the available options. This will control
155
172
- Google
156
173
-`free`
157
174
-`busy`
175
+
- ICS
176
+
-`free`
177
+
-`tentative`
178
+
-`busy`
158
179
159
180
#### `AVAILABLE_STATUS`
160
181
@@ -204,14 +225,17 @@ if webexStatus == const.Status.unknown or webexStatus in offStatus:
204
225
# Fall through to Slack
205
226
currentStatus = slackStatus
206
227
207
-
if (currentStatus in availableStatus or currentStatus in offStatus)
208
-
and (officeStatus notin offStatus or googleStatus notin offStatus):
228
+
if (currentStatus in availableStatus or currentStatus in offStatus)
229
+
and (officeStatus notin offStatus or googleStatus notin offStatus
230
+
or icsStatus notin offStatus):
209
231
210
-
# Office 365 currently takes precedence over Google
232
+
# Office 365 currently takes precedence over Google, Google over ICS
211
233
if (officeStatus != const.Status.unknown):
212
234
currentStatus = officeStatus
213
-
else:
235
+
elif (googleStatus != const.Status.unknown):
214
236
currentStatus = googleStatus
237
+
else:
238
+
currentStatus = icsStatus
215
239
216
240
if currentStatus in availableStatus:
217
241
# Get availableColor
@@ -304,11 +328,13 @@ In the example above, the Slack custom status would match (since it is a case-in
304
328
305
329
### **Tuya**
306
330
331
+
**Note:** Tuya configuration is only required when [`TARGET`](#target) is set to `tuya` (the default). If using `TARGET=virtual`, you can skip this section.
332
+
307
333
#### `TUYA_DEVICE`
308
334
309
-
-*Required*
335
+
-*Required if [`TARGET`](#target) is `tuya`*
310
336
311
-
Status-Light requires a [Tuya](https://www.tuya.com/)device, which are white-boxed and sold under many brand names. For example, the Tuya light working in the current environment is an [Above Lights](http://alabovelights.com/)[Smart Bulb 9W, model AL1](http://alabovelights.com/pd.jsp?id=17).
337
+
Status-Light supports [Tuya](https://www.tuya.com/)devices, which are white-boxed and sold under many brand names. For example, the Tuya light working in the current environment is an [Above Lights](http://alabovelights.com/)[Smart Bulb 9W, model AL1](http://alabovelights.com/pd.jsp?id=17).
312
338
313
339
Status-Light uses the [tuyaface](https://github.com/TradeFace/tuyaface/) module for Tuya communication.
314
340
@@ -326,13 +352,19 @@ Example `TUYA_DEVICE` value:
326
352
327
353
**Note:** Status-Light will accept an FQDN instead of IP, as long as the name can be resolved. Tuya devices will typically register themselves with the last 6 digits of the device ID, for example `ESP_xxxxxx.local`.
328
354
329
-
#### `TUYA_BRIGHTNESS`
355
+
#### `LIGHT_BRIGHTNESS`
330
356
331
357
-*Optional*
332
-
- Acceptable range: `32`-`255`
333
-
- Default value: `128`
358
+
- Acceptable range: `0`-`100` (percentage)
359
+
- Default value: `50`
360
+
361
+
Set the brightness of your RGB light as a percentage (0-100%). Status-Light defaults to 50% brightness.
334
362
335
-
Set the brightness of your Tuya light. This is an 8-bit `integer` corresponding to a percentage from 0%-100% (though Tuya lights typically don't accept a brightness value below `32`). Status-Light defaults to 50% brightness, `128`.
363
+
**Legacy Format Auto-Detection:** For backward compatibility, values above 100 (or in the range 32-100) are automatically detected as the legacy 0-255 format and converted to percentage. For example, `LIGHT_BRIGHTNESS=128` is auto-detected and converted to 50%. To avoid confusion, use percentage values (0-100) for new configurations.
364
+
365
+
**Note:** The legacy format was specific to Tuya's device scale. The new percentage format works with all light targets and is more intuitive.
366
+
367
+
**Backward Compatibility:**`TUYA_BRIGHTNESS` is still supported as an alias for `LIGHT_BRIGHTNESS` but is deprecated. Use `LIGHT_BRIGHTNESS` for new configurations.
336
368
337
369
---
338
370
@@ -452,6 +484,70 @@ Since Google has [deprecated](https://developers.googleblog.com/2022/02/making-o
452
484
453
485
---
454
486
487
+
### **ICS**
488
+
489
+
**Note:** See [`CALENDAR_LOOKAHEAD`](#calendar_lookahead) to configure lookahead timing for Calendar sources.
490
+
491
+
Status-Light uses the [icalendar](https://github.com/collective/icalendar) and [recurring-ical-events](https://github.com/niccokunzmann/python-recurring-ical-events) libraries to parse ICS files. These libraries correctly handle recurring events and cross-timezone event matching (e.g., a Pacific time event will be correctly detected when running in Mountain time).
492
+
493
+
Status-Light's ICS source implements **RFC 5545 compliant** status detection based on the `TRANSP` (transparency) and `STATUS` properties, **plus Microsoft CDO extensions** for enhanced Office 365/Outlook compatibility:
When present, the `X-MICROSOFT-CDO-BUSYSTATUS` property takes precedence and provides more granular status information:
507
+
508
+
| CDO BUSYSTATUS Property | Status-Light Status |
509
+
|------------------------|---------------------|
510
+
|`FREE` or `0`|`free`|
511
+
|`TENTATIVE` or `1`|`tentative`|
512
+
|`BUSY` or `2`|`busy`|
513
+
|`OOF` or `3`|`outofoffice` (away/out of office) |
514
+
|`WORKINGELSEWHERE` or `4`|`workingelsewhere` (remote work) |
515
+
516
+
**Status Precedence:** When multiple events exist in the lookahead window, the "busiest" status wins: `busy` > `outofoffice` > `workingelsewhere` > `tentative` > `free`.
517
+
518
+
#### `ICS_URL`
519
+
520
+
-*Required if `ics` is present in [`SOURCES`](#sources)*
521
+
522
+
The URL to an ICS (iCalendar) file. This can be any publicly accessible URL that returns a valid `.ics` file, such as:
523
+
- A shared Google Calendar ICS link
524
+
- An Office 365 published calendar
525
+
- Any other iCalendar-compatible calendar export
526
+
527
+
Status-Light will fetch this file periodically (controlled by `ICS_CACHELIFETIME`) and check for events within the [`CALENDAR_LOOKAHEAD`](#calendar_lookahead) window.
528
+
529
+
**Docker Secrets:** This variable can instead be specified in a secrets file, using the `ICS_URL_FILE` variable.
530
+
531
+
#### `ICS_CACHESTORE`
532
+
533
+
-*Optional, only valid if `ics` is present in [`SOURCES`](#sources)*
534
+
- Acceptable value: Any writable location on disk, e.g. `/path/to/cache/`
535
+
- Default value: `~`
536
+
537
+
Defines a writable location on disk where the cached ICS file is stored.
538
+
539
+
**Note:** This path is directory only. Status-Light will persist a file named `status-light-ics-cache.ics` within the directory supplied.
540
+
541
+
#### `ICS_CACHELIFETIME`
542
+
543
+
-*Optional, only valid if `ics` is present in [`SOURCES`](#sources)*
544
+
- Acceptable range: `5`-`60`
545
+
- Default value: `30`
546
+
547
+
Set the number of minutes the cached ICS file remains valid before being re-fetched from the URL. A lower value means more frequent updates but more network requests.
548
+
549
+
---
550
+
455
551
### **Active Times**
456
552
457
553
If you prefer to leave Status-Light running all the time (e.g. headless in a Docker container), you may wish to disable status polling during off hours.
0 commit comments