- Add `webuntis` event type; event creation resolves URL from system `supplement_table_url`
- Consolidate settings: remove separate webuntis-url endpoints; use GET/POST /api/system-settings/supplement-table
- Scheduler: emit top-level `event_type` and unified `website` payload (`{ "type":"browser","url":"..." }`) for website/webuntis
- Preserve presentation payloads (page_progress/auto_progress) — presentation messages remain backwards-compatible
- Update defaults (`init_defaults.py`) and remove duplicate webuntis setting
- Docs & metadata: bump program-info to 2025.1.0-alpha.13; update README, copilot-instructions, DEV- and TECH-CHANGELOGs; add MQTT_EVENT_PAYLOAD_GUIDE.md and WEBUNTIS_EVENT_IMPLEMENTATION.md
10 KiB
WebUntis Event Type Implementation
Date: 2025-10-19
Status: Completed
Summary
Implemented support for a new webuntis event type that displays a centrally-configured WebUntis website on infoscreen clients. This event type follows the same client-side behavior as website events but sources its URL from system settings rather than per-event configuration.
Changes Made
1. Database & Models
The webuntis event type was already defined in the EventType enum in models/models.py:
class EventType(enum.Enum):
presentation = "presentation"
website = "website"
video = "video"
message = "message"
other = "other"
webuntis = "webuntis" # Already present
2. System Settings
Default Initialization (server/init_defaults.py)
Updated supplement_table_url description to indicate it's used for both Vertretungsplan and WebUntis:
('supplement_table_url', '', 'URL für Vertretungsplan / WebUntis (Stundenplan-Änderungstabelle)')
This setting is automatically seeded during database initialization.
Note: The same URL (supplement_table_url) is used for both:
- Vertretungsplan (supplement table) displays
- WebUntis event displays
API Endpoints (server/routes/system_settings.py)
WebUntis events use the existing supplement table endpoints:
-
GET /api/system-settings/supplement-table(Admin+)- Returns:
{"url": "https://...", "enabled": true/false}
- Returns:
-
POST /api/system-settings/supplement-table(Admin+)- Body:
{"url": "https://...", "enabled": true/false} - Updates the URL used for both supplement table and WebUntis events
- Body:
No separate WebUntis URL endpoint is needed—the supplement table URL serves both purposes.
3. Event Creation (server/routes/events.py)
Added handling for webuntis event type in create_event():
# WebUntis: URL aus System-Einstellungen holen und EventMedia anlegen
if event_type == "webuntis":
# Hole WebUntis-URL aus Systemeinstellungen (verwendet supplement_table_url)
webuntis_setting = session.query(SystemSetting).filter_by(key='supplement_table_url').first()
webuntis_url = webuntis_setting.value if webuntis_setting else ''
if not webuntis_url:
return jsonify({"error": "WebUntis / Supplement table URL not configured in system settings"}), 400
# EventMedia für WebUntis anlegen
media = EventMedia(
media_type=MediaType.website,
url=webuntis_url,
file_path=webuntis_url
)
session.add(media)
session.commit()
event_media_id = media.id
Workflow:
- Check if
supplement_table_urlis configured in system settings - Return error if not configured
- Create
EventMediawithMediaType.websiteusing the supplement table URL - Associate the media with the event
4. Scheduler Payload (scheduler/db_utils.py)
Modified format_event_with_media() to handle both website and webuntis events:
# Handle website and webuntis events (both display a website)
elif event.event_type.value in ("website", "webuntis"):
event_dict["website"] = {
"type": "browser",
"url": media.url if media.url else None
}
if media.id not in _media_decision_logged:
logging.debug(
f"[Scheduler] Using website URL for event_media_id={media.id} (type={event.event_type.value}): {media.url}")
_media_decision_logged.add(media.id)
Key Points:
- Both event types use the same
websitepayload structure - Clients interpret
event_typebut handle display identically - URL is already resolved from system settings during event creation
5. Documentation
Created comprehensive documentation in MQTT_EVENT_PAYLOAD_GUIDE.md covering:
- MQTT message structure
- Event type-specific payloads
- Best practices for client implementation
- Versioning strategy
- System settings integration
MQTT Message Format
WebUntis Event Payload
{
"id": 125,
"event_type": "webuntis",
"title": "Schedule Display",
"start": "2025-10-19T09:00:00+00:00",
"end": "2025-10-19T09:30:00+00:00",
"group_id": 1,
"website": {
"type": "browser",
"url": "https://webuntis.example.com/schedule"
}
}
Website Event Payload (for comparison)
{
"id": 124,
"event_type": "website",
"title": "School Website",
"start": "2025-10-19T09:00:00+00:00",
"end": "2025-10-19T09:30:00+00:00",
"group_id": 1,
"website": {
"type": "browser",
"url": "https://example.com/page"
}
}
Client Implementation Guide
Clients should handle both website and webuntis event types identically:
function parseEvent(event) {
switch (event.event_type) {
case 'presentation':
return handlePresentation(event.presentation);
case 'website':
case 'webuntis':
// Both types use the same display logic
return handleWebsite(event.website);
case 'video':
return handleVideo(event.video);
default:
console.warn(`Unknown event type: ${event.event_type}`);
}
}
function handleWebsite(websiteData) {
// websiteData = { type: "browser", url: "https://..." }
if (!websiteData.url) {
console.error('Website event missing URL');
return;
}
// Display URL in embedded browser/webview
displayInBrowser(websiteData.url);
}
Best Practices
1. Type-Based Dispatch
Always check event_type first and dispatch to appropriate handlers. The nested payload structure (presentation, website, etc.) provides type-specific details.
2. Graceful Error Handling
- Validate URLs before displaying
- Handle missing or empty URLs gracefully
- Provide user-friendly error messages
3. Unified Website Display
Both website and webuntis events trigger the same browser/webview component. The only difference is in event creation (per-event URL vs. system-wide URL).
4. Extensibility
The message structure supports adding new event types without breaking existing clients:
- Old clients ignore unknown
event_typevalues - New fields in existing payloads are optional
- Nested objects isolate type-specific changes
Administrative Setup
Setting the WebUntis / Supplement Table URL
The same URL is used for both Vertretungsplan (supplement table) and WebUntis displays.
-
Via API (recommended for UI integration):
POST /api/system-settings/supplement-table { "url": "https://webuntis.example.com/schedule", "enabled": true } -
Via Database (for initial setup):
INSERT INTO system_settings (`key`, value, description) VALUES ('supplement_table_url', 'https://webuntis.example.com/schedule', 'URL für Vertretungsplan / WebUntis (Stundenplan-Änderungstabelle)'); -
Via Dashboard: Settings → Events → WebUntis / Vertretungsplan
Creating a WebUntis Event
Once the URL is configured, events can be created through:
- Dashboard UI: Select "WebUntis" as event type
- API:
POST /api/events { "group_id": 1, "title": "Daily Schedule", "description": "Current class schedule", "start": "2025-10-19T08:00:00Z", "end": "2025-10-19T16:00:00Z", "event_type": "webuntis", "created_by": 1 }
No website_url is required—it's automatically fetched from the supplement_table_url system setting.
Migration Notes
From Presentation-Only System
This implementation extends the existing event system without breaking presentation events:
- Presentation events: Still use
presentationpayload withfilesarray - Website/WebUntis events: Use new
websitepayload withurlfield - Message structure: Includes
event_typefor client-side dispatch
Future Event Types
The pattern established here can be extended to other event types:
- Video:
event_dict["video"] = { "type": "media", "url": "...", "autoplay": true } - Message:
event_dict["message"] = { "type": "html", "content": "..." } - Custom: Any new type with its own nested payload
Testing Checklist
- Database migration includes
webuntisenum value - System setting
supplement_table_urldescription updated to include WebUntis - Event creation validates supplement_table_url is configured
- Event creation creates
EventMediawith supplement table URL - Scheduler includes
websitepayload forwebuntisevents - MQTT message structure documented
- No duplicate webuntis_url setting (uses supplement_table_url)
- Dashboard UI shows supplement table URL is used for WebUntis (documentation)
- Client implementation tested with WebUntis events (client-side)
Related Files
Modified
scheduler/db_utils.py- Event formatting logicserver/routes/events.py- Event creation handlingserver/routes/system_settings.py- WebUntis URL endpointsserver/init_defaults.py- System setting defaults
Created
MQTT_EVENT_PAYLOAD_GUIDE.md- Comprehensive message format documentationWEBUNTIS_EVENT_IMPLEMENTATION.md- This file
Existing (Not Modified)
models/models.py- Already hadwebuntisenum valuedashboard/src/components/CustomEventModal.tsx- Already supports webuntis type
Further Enhancements
Short-term
- Add WebUntis URL configuration to dashboard Settings page
- Update event creation UI to explain WebUntis URL comes from settings
- Add validation/preview for WebUntis URL in settings
Long-term
- Support multiple WebUntis instances (per-school in multi-tenant setup)
- Add WebUntis-specific metadata (class filter, room filter, etc.)
- Implement iframe sandboxing options for security
- Add refresh intervals for dynamic WebUntis content
Conclusion
The webuntis event type is now fully integrated into the infoscreen system. It uses the existing supplement_table_url system setting, which serves dual purposes:
- Vertretungsplan (supplement table) displays in the existing settings UI
- WebUntis schedule displays via the webuntis event type
This provides a clean separation between system-wide URL configuration and per-event scheduling, while maintaining backward compatibility and following established patterns for event payload structure.
The implementation demonstrates best practices:
- Reuse existing infrastructure: Uses supplement_table_url instead of creating duplicate settings
- Consistency: Follows same patterns as existing event types
- Extensibility: Easy to add new event types following this model
- Documentation: Comprehensive guides for both developers and clients