Skip to content

Commit b47c61b

Browse files
Alexclaude
andcommitted
feat: add HDMI-CEC monitor control API
Add CEC status, standby, and wake endpoints to player API v2: - GET /api/v2/cec/status — returns CEC availability and TV power state - POST /api/v2/cec/standby — sends TV to standby via HDMI-CEC - POST /api/v2/cec/wake — wakes TV via HDMI-CEC Also adds get_status() method to CecController and removes state guards from standby/wake to allow manual override when state is out of sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7aac0e7 commit b47c61b

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

api/urls/v2.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
AssetsControlViewV2,
1515
AssetViewV2,
1616
BackupViewV2,
17+
CecStandbyViewV2,
18+
CecStatusViewV2,
19+
CecWakeViewV2,
1720
DeviceSettingsViewV2,
1821
FileAssetViewV2,
1922
InfoViewV2,
@@ -86,6 +89,22 @@ def get_url_patterns():
8689
ViewLogViewV2.as_view(),
8790
name='viewlog_v2',
8891
),
92+
# CEC TV control
93+
path(
94+
'v2/cec/status',
95+
CecStatusViewV2.as_view(),
96+
name='cec_status_v2',
97+
),
98+
path(
99+
'v2/cec/standby',
100+
CecStandbyViewV2.as_view(),
101+
name='cec_standby_v2',
102+
),
103+
path(
104+
'v2/cec/wake',
105+
CecWakeViewV2.as_view(),
106+
name='cec_wake_v2',
107+
),
89108
# Schedule slots
90109
path(
91110
'v2/schedule/slots',

api/views/v2.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,54 @@ def post(self, request):
875875
)
876876

877877

878+
# ── CEC TV control ──
879+
880+
_cec_instance = None
881+
882+
883+
def _get_cec():
884+
global _cec_instance
885+
if _cec_instance is None:
886+
import importlib.util
887+
_app_root = path.dirname(path.dirname(path.dirname(path.abspath(__file__))))
888+
spec = importlib.util.spec_from_file_location(
889+
'cec_controller',
890+
path.join(_app_root, 'viewer', 'cec_controller.py'),
891+
)
892+
mod = importlib.util.module_from_spec(spec)
893+
spec.loader.exec_module(mod)
894+
_cec_instance = mod.CecController()
895+
return _cec_instance
896+
897+
898+
class CecStatusViewV2(APIView):
899+
"""GET /api/v2/cec/status — CEC availability and TV power state."""
900+
901+
@authorized
902+
def get(self, request):
903+
return Response(_get_cec().get_status())
904+
905+
906+
class CecStandbyViewV2(APIView):
907+
"""POST /api/v2/cec/standby — Send TV to standby via HDMI-CEC."""
908+
909+
@authorized
910+
def post(self, request):
911+
cec = _get_cec()
912+
cec.standby()
913+
return Response(cec.get_status())
914+
915+
916+
class CecWakeViewV2(APIView):
917+
"""POST /api/v2/cec/wake — Wake TV via HDMI-CEC."""
918+
919+
@authorized
920+
def post(self, request):
921+
cec = _get_cec()
922+
cec.wake()
923+
return Response(cec.get_status())
924+
925+
878926
class ViewLogViewV2(APIView):
879927
"""GET /api/v2/viewlog — playback history from viewlog.db."""
880928

viewer/cec_controller.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@ def _detect_device(self):
4242

4343
logging.warning('CEC: no working device found, TV control disabled')
4444

45+
def get_status(self):
46+
"""Return CEC availability and TV power state."""
47+
return {'cec_available': self._available, 'tv_on': self._tv_is_on}
48+
4549
def standby(self):
46-
"""Send TV to standby. No-op if already off or CEC unavailable."""
47-
if not self._available or not self._tv_is_on:
50+
"""Send TV to standby. No-op if CEC unavailable."""
51+
if not self._available:
4852
return
4953
try:
5054
subprocess.run(
@@ -57,8 +61,8 @@ def standby(self):
5761
logging.warning('CEC: standby failed: %s', e)
5862

5963
def wake(self):
60-
"""Wake TV up. No-op if already on or CEC unavailable."""
61-
if not self._available or self._tv_is_on:
64+
"""Wake TV up. No-op if CEC unavailable."""
65+
if not self._available:
6266
return
6367
try:
6468
subprocess.run(

0 commit comments

Comments
 (0)