Files
infoscreen-dev/HDMI_CEC_FLOW_DIAGRAM.md
RobbStarkAustria 6617c3d7b9 HDMI-CEC: auto-disable in dev mode + docs/tests synced
README: document dev-mode CEC auto-disable; add testing notes; set CEC_POWER_OFF_WAIT=5
Copilot instructions: add dev-mode CEC behavior and test script guidance
test-hdmi-cec.sh: respect ENV=development (skip option 5), explicit .env load, ASCII output, 30s wait
display_manager: remove emoji from logs to avoid Unicode errors
2025-11-12 17:09:11 +01:00

422 lines
15 KiB
Markdown

# HDMI-CEC Integration Flow Diagram
## System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Infoscreen Client System │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ MQTT Client │◄───────►│Display Manager│ │
│ │ (simclient) │ │ │ │
│ └──────┬───────┘ └───────┬──────┘ │
│ │ │ │
│ │ Writes Events │ Reads Events │
│ ▼ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ current_event.json │ │
│ └────────────────────────────────────┘ │
│ │ │
│ Display Manager Components │ │
│ ┌──────────────────────────────┴─────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ Event Handler│ │HDMI CEC Control│ │
│ │ │───────────────────────────│ │ │
│ │ - Reads │ 1. Turn TV ON when │ - turn_on() │ │
│ │ - Validates │ event starts │ - turn_off() │ │
│ │ - Schedules │ 2. Cancel turn-off │ - cancel() │ │
│ │ │ when active │ │ │
│ └──────┬───────┘ 3. Schedule turn-off └────────┬───────┘ │
│ │ when ends │ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │Display Processes│ │ cec-client │ │
│ │- Impressive │ │ (binary) │ │
│ │- Chromium │ └───────┬────────┘ │
│ │- VLC │ │ │
│ └────────────────┘ │ │
│ │ │
└──────────────────────────────────────────────────────┼──────────┘
┌────────▼────────┐
│ HDMI-CEC Bus │
└────────┬────────┘
┌────────▼────────┐
│ TV Display │
│ │
│ ON ◄─────► OFF │
└─────────────────┘
```
## Event Lifecycle with CEC
### 1. Event Starts
```
MQTT Event Arrives
├─► Write to current_event.json
Display Manager Detects New Event
├─► Turn TV ON via CEC ──► echo "on 0" | cec-client -s -d 1
│ │
├─► Cancel any pending │
│ turn-off timer ▼
│ TV Powers ON
Start Display Process (Impressive/Chromium/VLC)
```
### 2. Event Active (Continuous)
```
Main Loop (every 5s)
├─► Check if event still active
├─► Process still running?
└─► Cancel turn-off timer ──► Keep TV ON
(prevents premature shutdown)
```
### 3. Event Ends
```
No Active Event Detected
├─► Stop Display Process
Schedule TV Turn-Off (with delay)
├─► threading.Timer(30s)
[DELAY]
├─► Can be cancelled if new event arrives
Delay Expires
Turn TV OFF ──► echo "standby 0" | cec-client -s -d 1
TV Powers OFF (Standby)
```
### 4. Event Switching (No TV Off)
```
Event A Running
Event B Arrives
├─► Stop Event A Display (turn_off_tv=False)
├─► TV stays ON
├─► Cancel any pending turn-off
Start Event B Display
└─► TV remains ON (seamless transition)
```
## CEC Command Flow
### Turn On Sequence
```
Python Code Shell Command CEC Bus
───────────── ────────────── ─────────
turn_on() called
├─► Check if already ON ──► Skip if tv_state == True
├─► Build command
subprocess.run() ──────────► echo "on 0" | ────────► CEC: Power On (0x04)
cec-client -s -d 1 Device 0 (TV)
│ │
▼ ▼
Parse output STDOUT: "power status changed..."
├─► Success indicators found?
├─► Update tv_state = True
└─► Return True
```
### Turn Off (Delayed) Sequence
```
Python Code Timer Thread Shell Command
───────────── ────────────── ──────────────
turn_off(delayed=True)
├─► Create threading.Timer(30s, _turn_off_now)
├─► timer.daemon = True
└─► timer.start()
│ [30 seconds pass]
_turn_off_now() ──────────► subprocess.run()
│ │
│ ▼
│ echo "standby 0" | cec-client -s -d 1
│ │
▼ ▼
Update tv_state = False TV enters standby
```
### Cancel Turn-Off Sequence
```
Timer Active (counting down)
cancel_turn_off() called
├─► timer.cancel()
└─► Timer stopped, TV stays ON
```
## State Diagram
```
┌──────────────┐
│ System Init │
└──────┬───────┘
┌──────────────┐
┌───────────│ Detect TV │───────────┐
│ │ State │ │
│ └──────────────┘ │
│ │
┌──────▼─────┐ ┌──────▼─────┐
│ TV is ON │ │ TV is OFF │
│ (or unknown)│ │ (standby) │
└──────┬─────┘ └──────┬─────┘
│ │
┌──────────┴──────────────────────────────────────┘
│ Event Arrives
┌────────────┐
│ Turn TV ON │────► CEC Command: "on 0"
└──────┬─────┘
┌────────────┐
│ TV is ON │
│ Event Active│◄────┐
└──────┬─────┘ │
│ │
│ Event │ Event Still
│ Ends │ Running
│ │
▼ │
┌────────────┐ │
│Start Timer │ │
│ (30s) │───────┘
└──────┬─────┘ Cancel Timer
│ Timer
│ Expires
┌────────────┐
│Turn TV OFF │────► CEC Command: "standby 0"
└──────┬─────┘
┌────────────┐
│ TV is OFF │
│ (standby) │
└────────────┘
│ New Event
└─────────► Turn TV ON (cycle repeats)
```
## Configuration Impact
### CEC_TURN_OFF_DELAY Values
```
Delay = 0 seconds (Immediate)
────────────────────────────────
Event End ──► TV OFF (no delay)
⚠️ May cause flicker if events are close together
Delay = 30 seconds (Default)
────────────────────────────────
Event End ──► [Wait 30s] ──► TV OFF
✓ Balanced approach
✓ Prevents flicker for events within 30s
✓ Still saves power
Delay = 120 seconds (Conservative)
────────────────────────────────
Event End ──► [Wait 2m] ──► TV OFF
✓ Very smooth for frequent events
⚠️ May waste power if events are sparse
```
## Integration Points in Code
### 1. Initialization (DisplayManager.__init__)
```python
self.cec = HDMICECController(
enabled=CEC_ENABLED,
device=CEC_DEVICE,
turn_off_delay=CEC_TURN_OFF_DELAY
)
```
### 2. Signal Handler (Graceful Shutdown)
```python
def _signal_handler(self, signum, frame):
self.stop_current_display()
self.cec.turn_off(delayed=True) # TV off on shutdown
sys.exit(0)
```
### 3. Event Processing (Main Loop)
```python
# Turn on TV before starting display
self.cec.turn_on()
# During event
self.cec.cancel_turn_off() # Keep TV on
# When stopping
self.stop_current_display(turn_off_tv=True)
```
### 4. Stop Display
```python
def stop_current_display(self, turn_off_tv: bool = True):
# ... terminate process ...
if turn_off_tv:
self.cec.turn_off(delayed=True)
```
## Logging Flow
```
Display Manager Startup
├──► [INFO] HDMI-CEC controller initialized (device: TV, turn_off_delay: 30s)
├──► [INFO] TV detected as ON
└──► [INFO] Display Manager starting...
Event Arrives
├──► [INFO] Starting display for event: presentation_slides.pdf
├──► [INFO] Turning TV ON via HDMI-CEC...
├──► [DEBUG] CEC command 'on 0' executed successfully
├──► [INFO] TV turned ON successfully
└──► [INFO] Display started successfully
Event Ends
├──► [INFO] No active events - stopping current display
├──► [INFO] Scheduling TV turn-off in 30s...
└──► [INFO] Stopping current display
30 Seconds Later
├──► [INFO] Turning TV OFF via HDMI-CEC...
├──► [DEBUG] CEC command 'standby 0' executed successfully
└──► [INFO] TV turned OFF successfully
New Event Before Timeout
├──► [DEBUG] Cancelled TV turn-off timer
└──► [INFO] Turning TV ON via HDMI-CEC...
```
## Error Scenarios
### 1. cec-client Not Installed
```
HDMICECController.__init__()
├──► _check_cec_available()
├──► [WARNING] cec-client not found - HDMI-CEC control disabled
├──► [INFO] Install with: sudo apt-get install cec-utils
└──► self.enabled = False
(System continues without TV control)
```
### 2. TV Not Responding
```
turn_on() called
├──► subprocess.run(...)
├──► Exit code != 0
├──► [WARNING] CEC command 'on 0' may have failed
└──► Return False
(Logged but not critical)
```
### 3. CEC Command Timeout
```
_run_cec_command("on 0")
├──► subprocess.run(..., timeout=5)
├──► TimeoutExpired after 5s
├──► [ERROR] CEC command 'on 0' timed out after 5s
└──► Return False
```
## Summary
The HDMI-CEC integration provides:
- ✅ Automatic TV control based on events
- ✅ Configurable behavior via environment variables
- ✅ Smart delay mechanism to prevent flicker
- ✅ Graceful fallback if CEC unavailable
- ✅ Comprehensive logging for troubleshooting
- ✅ Minimal performance impact
- ✅ Production-ready reliability