Modular architecture with abstract targets and enhanced ICS support#87
Merged
PortableProgrammer merged 14 commits intomainfrom Feb 7, 2026
Merged
Modular architecture with abstract targets and enhanced ICS support#87PortableProgrammer merged 14 commits intomainfrom
PortableProgrammer merged 14 commits intomainfrom
Conversation
Provides project overview, run commands, architecture documentation, and configuration details for AI-assisted development. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements support for ICS calendar files as a status source: - Fetches and caches ICS files from a URL - Parses events using icalevents library - Returns BUSY if events exist in lookahead window, FREE otherwise - Cache lifetime configurable (5-60 minutes, default 30) - Priority: Office 365 > Google > ICS Environment variables: - ICS_URL: URL to ICS calendar file (required, supports _FILE for secrets) - ICS_CACHESTORE: Directory for cached file (default: ~) - ICS_CACHELIFETIME: Cache validity in minutes (default: 30) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Virtual Light Target: - Add `targets/virtual.py` with VirtualLight class for testing without hardware - Add `TARGET` environment variable (tuya|virtual) to select output target - TUYA_DEVICE is now only required when TARGET=tuya ICS Enhancements: - Implement RFC 5545 compliant status mapping based on TRANSP and STATUS properties - TRANSP=TRANSPARENT or STATUS=CANCELLED → free - STATUS=TENTATIVE → tentative (was previously treated as busy) - STATUS=CONFIRMED or unset → busy - Apply precedence when multiple events exist: busy > tentative > free Documentation: - Add ICS source documentation to README.md - Add TARGET documentation to README.md - Update SOURCES list and status examples Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code cleanup: - Remove redundant `import os.path` (os.path is part of os module) - Simplify empty list check in get_current_status() - Update copyright year in virtual.py to 2025 CLAUDE.md updates: - Mark ICS as RFC 5545 compliant (no longer "in development") - Add ICS to calendar source priority (Office 365 > Google > ICS) - Add virtual.py to Output Targets section - Add `ics` to SOURCES list and `TARGET` to Configuration - Update status priority to include Tentative Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The icalevents library had bugs with timezone handling where events in other timezones were filtered based on their original timezone's time rather than the converted local time. This caused cross-timezone events to be missed (e.g., a 9:00 AM Pacific event wouldn't be found when searching from 9:30 AM Mountain, even though it runs until 10:00 AM Mountain). Switched to icalendar + recurring-ical-events which correctly handle: - Cross-timezone event matching - Recurring event expansion - RFC 5545 compliant parsing Updated documentation: - README.md: Added note about libraries and timezone support - CLAUDE.md: Updated ICS description, added Documentation section Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add note that Tuya section can be skipped when using virtual target - Update TUYA_DEVICE requirement to be conditional on TARGET=tuya - Update TUYA_BRIGHTNESS to indicate it only applies when TARGET=tuya Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
[feat] ICS - Experimenting with Claude Code
Major Changes: - Extract status precedence logic into new utility/precedence.py module - Add Microsoft CDO extension support to ICS calendar source - Enhance logging throughout precedence selection process ICS Enhancements: - Support X-MICROSOFT-CDO-BUSYSTATUS property (Office 365/Outlook) - Map OOF (Out of Office) to Status.OUTOFOFFICE - Map WORKINGELSEWHERE to Status.WORKINGELSEWHERE - Update status precedence: BUSY > OUTOFOFFICE > WORKINGELSEWHERE > TENTATIVE > FREE - Add comprehensive logging for CDO status detection Precedence Module (utility/precedence.py): - New select_status() function with dictionary-based approach - Enhanced debug logging showing input sources and decision flow - Logs collaboration sources, calendar sources, and final selection - More extensible architecture for adding new sources Documentation Updates: - README.md: Document ICS CDO extensions and new status mappings - README.md: Update ICS status precedence documentation - CLAUDE.md: Add precedence.py to Key Utilities section - CLAUDE.md: Simplify documentation maintenance section Other Changes: - Update copyright years to 2026 across all source files - Refactor status-light.py to use precedence module - Replace 35 lines of if/elif chains with clean module call Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Major Changes: - Create abstract LightTarget base class for all light targets - Convert brightness from device-specific (0-255) to percentage (0-100) - Auto-detect and convert legacy brightness format for backward compatibility - Remove Tuya-specific dependencies from main codebase Abstract Target Interface (targets/base.py): - New LightTarget abstract base class with set_color() and turn_off() methods - Brightness now percentage-based (0-100) across all targets - Provides validate_color() and validate_brightness() helpers - Implementations convert percentage to device-specific formats Tuya Target Updates (targets/tuya.py): - Extends LightTarget abstract base class - Locked to COLOR mode internally (no white temperature mode exposed) - Converts percentage brightness to Tuya's 0-255 DPS format - Renamed internal method: set_status() → _set_dps() for clarity - Validates inputs using base class helpers Virtual Target Updates (targets/virtual.py): - Extends LightTarget abstract base class - Simplified to color + brightness only (removed mode tracking) - Updated logging to reflect new interface Brightness Percentage Conversion: - Changed LIGHT_BRIGHTNESS from 0-255 to 0-100 percentage - Default changed from 128 → 50 (same brightness, clearer intent) - Conservative auto-detection for legacy format (>100 or 32-100 range) - Logs conversion when legacy format detected - Each target converts percentage to device-specific scale Main Code Updates (status-light.py): - Removed enum.TuyaMode references (no longer needed) - Changed light.set_status(mode, color, brightness) → light.set_color(color, brightness) - Changed light.off() → light.turn_off() - Removed unnecessary get_status() call during initialization - Now uses light_brightness (percentage) instead of tuya_brightness Environment Variables: - LIGHT_BRIGHTNESS: Now accepts 0-100 percentage (default: 50) - Legacy format (0-255) auto-detected and converted with informative logging - TUYA_BRIGHTNESS: Still supported as deprecated alias - Validation updated to expect 0-100 range Documentation Updates: - README.md: Document percentage format and legacy auto-detection - README.md: Update example from 128 → 50 - README.md: Explain backward compatibility - CLAUDE.md: Document abstract interface architecture - CLAUDE.md: Update configuration section with percentage format Benefits: - More intuitive brightness setting (percentage vs device scale) - Target-agnostic interface (easy to add Hue, LIFX, etc.) - Cleaner main code (no Tuya-specific knowledge) - Backward compatible (auto-converts legacy values) - Better extensibility (new targets implement 2 methods) Breaking Changes: - None for existing users (auto-detection handles legacy format) - Edge case: Values 0-31 treated as percentage (unlikely scenario) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
ARMv6 (Raspberry Pi 1) lacks hardware floating point and cannot build modern cryptography dependencies that require Rust toolchain. The Rust installer incorrectly targets ARMv7 hard-float, causing build failures. Retained platforms: linux/amd64, linux/arm/v7, linux/arm64 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The cffi package requires libffi development headers to compile its C extensions. Without libffi-dev, builds fail with: fatal error: ffi.h: No such file or directory Added libffi-dev to both install and purge steps to keep image size minimal while enabling successful builds on all platforms. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modular Architecture with Abstract Targets and Enhanced ICS Support
This release introduces significant architectural improvements and new features while maintaining backward compatibility.
Major Changes
1. Modular Status Precedence Logic
Issue: Hardcoded 35-line if/elif chain for status selection made it difficult to add new sources or test precedence rules.
Solution: Extracted precedence logic into new
utility/precedence.pymodule with dictionary-based approach.Benefits:
Details:
precedence.select_status()function with comprehensive logging2. Abstract Light Target Interface
Issue: Main codebase contained Tuya-specific dependencies (TuyaMode, device-specific brightness scale, set_status signature).
Solution: Created abstract
LightTargetbase class with simple, device-agnostic interface.Benefits:
Interface:
Implementations:
targets/base.py- Abstract interface definitiontargets/tuya.py- Tuya implementation (locked to COLOR mode, converts percentage to 0-255)targets/virtual.py- Virtual implementation (simplified, removed mode tracking)3. Percentage-Based Brightness
Issue: Brightness used Tuya's device-specific 0-255 scale, not intuitive for users or portable across devices.
Solution: Changed to percentage (0-100) with automatic legacy format detection.
Benefits:
Migration:
TUYA_BRIGHTNESSstill supported as deprecated aliasEnvironment Variables:
LIGHT_BRIGHTNESS- New percentage-based setting (0-100, default: 50)TUYA_BRIGHTNESS- Deprecated alias, still works4. Enhanced ICS Calendar Support
Issue: ICS source only supported basic RFC 5545 properties, missing Office 365/Outlook's granular status information.
Solution: Added Microsoft CDO extension support for enhanced status detection.
Benefits:
New Status Mappings:
X-MICROSOFT-CDO-BUSYSTATUS=OOF→ OUTOFOFFICE (away/out of office)X-MICROSOFT-CDO-BUSYSTATUS=WORKINGELSEWHERE→ WORKINGELSEWHERE (remote work)X-MICROSOFT-CDO-BUSYSTATUS=FREE→ FREEX-MICROSOFT-CDO-BUSYSTATUS=TENTATIVE→ TENTATIVEX-MICROSOFT-CDO-BUSYSTATUS=BUSY→ BUSYUpdated Precedence:
BUSY > TENTATIVE > FREEBUSY > OUTOFOFFICE > WORKINGELSEWHERE > TENTATIVE > FREE5. Enhanced Logging
Precedence Module:
ICS Source:
Documentation Updates
Breaking Changes
None for existing users!
TUYA_BRIGHTNESSstill works (deprecated)Edge Cases:
Migration Guide
No action required - existing configurations work as-is:
TUYA_BRIGHTNESS=128→ Auto-converts to 50%TUYA_BRIGHTNESS=255→ Auto-converts to 100%Recommended updates:
TUYA_BRIGHTNESS→LIGHT_BRIGHTNESS(0-100 percentage)LIGHT_BRIGHTNESS=50(same as oldTUYA_BRIGHTNESS=128)Testing Performed
✅ Docker build succeeds (multi-platform)
✅ Python syntax validation
✅ Code linting (line length, style)
✅ ICS with Office 365 calendar (OOF events tested)
✅ Virtual target (logging verified)
✅ Tuya target (shutdown sequence verified)
✅ Brightness auto-detection (default and legacy values)
✅ Signal handling (clean shutdown)
Files Changed
New Files:
status-light/targets/base.py(79 lines) - Abstract LightTarget interfacestatus-light/utility/precedence.py(144 lines) - Status precedence moduleModified Files:
status-light/status-light.py- Uses abstract interface, precedence modulestatus-light/targets/tuya.py- Implements LightTarget, percentage conversionstatus-light/targets/virtual.py- Implements LightTarget, simplifiedstatus-light/sources/calendar/ics.py- CDO extension supportstatus-light/utility/env.py- Percentage brightness with auto-detectionCLAUDE.md- Architecture documentation updatesREADME.md- User-facing documentation updatesStatistics:
Future Enhancements Enabled
This release makes it easy to:
Related Issues
Closes #74 (Log enums as names, not values)
Addresses TODO in status-light.py line 237 (build real precedence module)
Release Checklist:
Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com