This document outlines the current state, improvement opportunities, and future roadmap for the Stream Deck iCal Plugin based on comprehensive analysis of the codebase, architecture patterns, and user needs.
┌─────────────────────────────────────────────────────────────────┐
│ Stream Deck Software │
├─────────────────────────────────────────────────────────────────┤
│ Property Inspector (PI) │ Plugin (Node.js Runtime) │
│ ├─ pi.html / pi.js │ ├─ plugin.ts (entry) │
│ └─ setup.html / setup.js │ ├─ actions/ │
│ (Settings Popup) │ │ ├─ base-action.ts │
│ │ │ ├─ next-meeting.ts │
│ WebSocket ←───────────────→ │ │ ├─ time-left.ts │
│ │ │ └─ combined-action.ts │
│ │ └─ services/ │
│ │ ├─ calendar-manager.ts │
│ │ ├─ calendar-service.ts │
│ │ └─ ical-parser.ts │
└─────────────────────────────────────────────────────────────────┘
| Area | Description |
|---|---|
| Multi-Calendar | Named calendars with per-button selection, URL-based caching |
| Provider Support | Google, Outlook/O365, Apple Calendar with timezone handling |
| Recurring Events | Full RRULE/EXDATE support via rrule library |
| Test Coverage | 529 tests covering actions, services, and edge cases |
| SingletonAction | Proper per-button state management with Maps |
| User Experience | Configurable thresholds, flash alerts, marquee titles |
| Issue | Impact | Effort |
|---|---|---|
| Circular dependencies in rrule | Build warnings (harmless) | Low |
| No offline caching | Events lost on network failure | Medium |
| Settings popup separate from PI | Extra click for users | Medium |
| No event details view | Limited information display | Medium |
| No Stream Deck+ support | Missing dial/touchscreen features | High |
Current: Users click "Settings" button to open popup window. Proposed: Move calendar management inline to Property Inspector.
Benefits:
- Fewer clicks to configure
- More native Stream Deck feel
- Eliminates popup window management
Challenges:
- Limited PI height
- Need collapsible sections
- Complex state management
Current: No visual indication of sync status on buttons. Proposed: Add sync indicator overlay or status in PI.
interface SyncStatus {
lastSync: Date;
nextSync: Date;
status: 'syncing' | 'synced' | 'error' | 'offline';
eventCount: number;
}Current: Users can't see upcoming events without adding button. Proposed: Show mini event list in Property Inspector.
┌──────────────────────────────┐
│ Calendar: Work [▼] │
├──────────────────────────────┤
│ Upcoming Events: │
│ • 10:00 Team Standup │
│ • 14:00 1:1 with Manager │
│ • 16:30 Sprint Review │
└──────────────────────────────┘
Current: Buttons only show time/countdown. Proposed: Long-press or separate action to join meeting.
interface CalendarEvent {
// ... existing fields
meetingUrl?: string; // Zoom, Teams, Meet link
location?: string; // Physical or virtual location
conferenceType?: 'zoom' | 'teams' | 'meet' | 'webex' | 'other';
}Implementation:
- Parse meeting URLs from event description/location
- Add
onLongPresshandler to open URL - Show video icon overlay when meeting has join link
Current: Events cleared when fetch fails. Proposed: Persist last successful fetch to disk.
interface CacheEntry {
url: string;
events: CalendarEvent[];
fetchedAt: Date;
expiresAt: Date;
etag?: string; // For conditional requests
}
// Use Node.js fs to persist
const CACHE_FILE = path.join(dataDir, 'calendar-cache.json');Current: Full fetch every 10 minutes. Proposed: Use ETag/If-Modified-Since for efficient updates.
const response = await fetch(url, {
headers: {
'If-None-Match': cachedEtag,
'If-Modified-Since': lastModified
}
});
if (response.status === 304) {
// Use cached data
}Current: Same event from multiple calendars shows multiple times. Proposed: Deduplicate by UID across calendars.
function deduplicateEvents(events: CalendarEvent[]): CalendarEvent[] {
const seen = new Map<string, CalendarEvent>();
for (const event of events) {
const key = `${event.uid}-${event.start.getTime()}`;
if (!seen.has(key)) {
seen.set(key, event);
}
}
return Array.from(seen.values());
}Current: Refresh blocks UI briefly. Proposed: Use worker threads for parsing.
Opportunity: Utilize dial and touchscreen on Stream Deck+.
Dial Features:
- Rotate to scroll through events
- Press to show/hide details
- Touch LCD for event list
Implementation:
// New action type for Stream Deck+ encoder
@action({ UUID: "com.pedrofuentes.ical.encoder" })
export class CalendarEncoderAction extends SingletonAction {
onDialRotate(ev: DialRotateEvent): void {
// Scroll through events
}
onDialPress(ev: DialPressEvent): void {
// Show event details on LCD
}
onTouchTap(ev: TouchTapEvent): void {
// Join meeting or show more
}
}Current: Each button shows one calendar. Proposed: Option to aggregate multiple calendars on one button.
interface ActionSettings {
calendarIds?: string[]; // Multiple calendar IDs
aggregationMode?: 'merge' | 'priority';
}Current: Visual flash only. Proposed: Optional audio alerts for meeting start.
interface GlobalSettings {
// ... existing
soundEnabled?: boolean;
soundFile?: string; // Custom sound path
soundVolume?: number; // 0-100
}Current: All calendars use same icon colors. Proposed: Match button color to calendar color.
interface NamedCalendar {
// ... existing
color?: string; // Hex color from calendar provider
}
// Generate tinted icons dynamically or use overlayProposed: New action showing multiple upcoming events.
┌─────────────┐
│ TODAY │
│ 10:00 Mtg 1 │
│ 14:00 Mtg 2 │
│ 16:30 Mtg 3 │
└─────────────┘
Current: Generic "Error" or "Loading" states. Proposed: Specific, actionable error messages.
| Error | Current | Proposed |
|---|---|---|
| Invalid URL | "Error" | "Invalid URL format" |
| Network fail | "Error" | "Can't reach calendar" |
| Auth expired | "Error" | "Re-authenticate" |
| Parse error | "Error" | "Calendar format error" |
Proposed: First-run wizard for new users.
Step 1: Select calendar provider (Google/Outlook/Apple)
Step 2: Show provider-specific URL instructions
Step 3: Paste and validate URL
Step 4: Choose default settings
Step 5: Done! Drag an action to get started.
Proposed: Backup/restore calendar configurations.
interface ExportedSettings {
version: string;
calendars: NamedCalendar[];
preferences: GlobalSettings;
// Exclude URLs for security, or encrypt
}Focus: UX improvements and bug fixes
- Better error messages with actionable guidance
- Meeting join URL detection and long-press to join
- Event preview in Property Inspector
- Calendar sync status indicator
- Fix any reported issues from v2.3.0
Estimated effort: 2-3 weeks
Focus: New hardware support
- Encoder action for dial control
- Touch LCD event list display
- Dial rotation for event scrolling
- Touch to join meetings
Estimated effort: 3-4 weeks
Focus: Power user features
- Multi-calendar aggregation on single button
- Offline caching with persistence
- Calendar color coding
- Agenda view action
- Notification sounds
Estimated effort: 4-6 weeks
Focus: Extended integrations
- Direct OAuth for Google/Microsoft (no URL copy)
- Two-way sync (mark as attended?)
- Slack/Teams status integration
- Focus mode integration (DND during meetings)
Estimated effort: 6-8 weeks
- Type Safety: Continue using strict TypeScript
- Testing: Maintain >80% coverage, add E2E tests
- Documentation: Keep copilot-instructions.md updated
- Changelog: Document all changes for users
- Bundle Size: Consider code splitting for PI
- Memory: Monitor for leaks with many calendars
- Network: Implement request throttling/debouncing
- URL Storage: Consider encrypting calendar URLs
- OAuth Tokens: If adding OAuth, use secure storage
- Input Validation: Sanitize all user inputs
Based on GitHub issues and common calendar plugin patterns:
| Theme | Frequency | Addressed |
|---|---|---|
| Multiple calendars | High | ✅ v2.3.0 |
| Recurring events | High | ✅ v2.0.0 |
| Meeting join links | Medium | 🔜 Proposed |
| Outlook support | Medium | ✅ v2.0.0 |
| Custom thresholds | Medium | ✅ v2.3.0 |
| Stream Deck+ | Medium | 🔜 Proposed |
| Offline mode | Low | 🔜 Proposed |
| Sound alerts | Low | 🔜 Proposed |
The Stream Deck iCal Plugin has evolved into a robust calendar integration with strong fundamentals. The Named Calendars feature in v2.3.0 addresses the most requested capability.
The recommended next focus areas are:
- Meeting join links - High value, medium effort
- Stream Deck+ support - Growing hardware base
- Better error handling - Reduces support burden
The architecture is well-positioned for these enhancements without major refactoring.
Last updated: February 2026 Maintainer: Pedro Fuentes