Files
service.video.hdrnotify/service.video.hdrnotify/service.py
T
oleksandr c0834979ea
Build Kodi Addon / build (push) Successful in 4s
feat: add HDR Notify service addon for Kodi
Kodi service addon that detects HDR/SDR state of the currently playing
video and notifies external services:

- HyperHDR: toggles tone mapping via videomodehdr JSON-RPC command
- Home Assistant: sends HDR state via webhook trigger

Supports Kodi 20 (Nexus) and later. Both targets are independently
configurable with host, port, SSL, and authentication settings.

Includes GitHub Actions workflow to build installable ZIP packages
on every push and publish them as release assets on tags.
2026-06-01 22:20:15 +03:00

135 lines
3.9 KiB
Python

import json
import ssl
import urllib.request
import xbmc
import xbmcaddon
ADDON_ID = "service.video.hdrnotify"
LOG_PREFIX = "[HDR Notify]"
def log(msg, level=xbmc.LOGINFO):
xbmc.log(f"{LOG_PREFIX} {msg}", level)
def read_settings():
addon = xbmcaddon.Addon(ADDON_ID)
return {
"hyperhdr_enabled": addon.getSettingBool("hyperhdr_enabled"),
"hyperhdr_host": addon.getSetting("hyperhdr_host"),
"hyperhdr_port": addon.getSettingInt("hyperhdr_port"),
"hyperhdr_ssl": addon.getSettingBool("hyperhdr_ssl"),
"hyperhdr_token": addon.getSetting("hyperhdr_token"),
"ha_enabled": addon.getSettingBool("ha_enabled"),
"ha_host": addon.getSetting("ha_host"),
"ha_port": addon.getSettingInt("ha_port"),
"ha_ssl": addon.getSettingBool("ha_ssl"),
"ha_webhook_id": addon.getSetting("ha_webhook_id"),
}
def _post_json(url, data, use_ssl=False):
payload = json.dumps(data).encode("utf-8")
req = urllib.request.Request(
url, data=payload, headers={"Content-Type": "application/json"}
)
ctx = None
if use_ssl:
ctx = ssl.create_default_context()
urllib.request.urlopen(req, timeout=2, context=ctx)
def notify_hyperhdr(settings, is_hdr):
host = settings["hyperhdr_host"]
port = settings["hyperhdr_port"]
if not host:
return
scheme = "https" if settings["hyperhdr_ssl"] else "http"
url = f"{scheme}://{host}:{port}/json-rpc"
use_ssl = settings["hyperhdr_ssl"]
try:
token = settings["hyperhdr_token"]
if token:
_post_json(
url,
{"command": "authorize", "subcommand": "login", "token": token},
use_ssl,
)
_post_json(
url,
{"command": "videomodehdr", "tan": 1, "HDR": 1 if is_hdr else 0},
use_ssl,
)
log(f"HyperHDR notified: HDR={'on' if is_hdr else 'off'}")
except Exception as e:
log(f"HyperHDR error: {e}", xbmc.LOGWARNING)
def notify_homeassistant(settings, is_hdr, hdr_type):
host = settings["ha_host"]
webhook_id = settings["ha_webhook_id"]
if not host or not webhook_id:
return
port = settings["ha_port"]
scheme = "https" if settings["ha_ssl"] else "http"
url = f"{scheme}://{host}:{port}/api/webhook/{webhook_id}"
try:
_post_json(
url,
{"hdr": is_hdr, "hdr_type": hdr_type},
settings["ha_ssl"],
)
log(f"Home Assistant notified: hdr={is_hdr}, hdr_type='{hdr_type}'")
except Exception as e:
log(f"Home Assistant error: {e}", xbmc.LOGWARNING)
def notify_targets(settings, is_hdr, hdr_type=""):
if settings["hyperhdr_enabled"]:
notify_hyperhdr(settings, is_hdr)
if settings["ha_enabled"]:
notify_homeassistant(settings, is_hdr, hdr_type)
class HDRMonitor(xbmc.Monitor):
def __init__(self):
super().__init__()
self.settings = read_settings()
def onSettingsChanged(self):
self.settings = read_settings()
log("Settings reloaded")
class HDRPlayer(xbmc.Player):
def __init__(self, monitor):
super().__init__()
self._monitor = monitor
def onAVStarted(self):
hdr_type = xbmc.getInfoLabel("VideoPlayer.HdrType")
is_hdr = bool(hdr_type)
log(f"Playback started: hdr_type='{hdr_type}', is_hdr={is_hdr}")
notify_targets(self._monitor.settings, is_hdr, hdr_type)
def onPlayBackStopped(self):
log("Playback stopped")
notify_targets(self._monitor.settings, False)
def onPlayBackEnded(self):
log("Playback ended")
notify_targets(self._monitor.settings, False)
if __name__ == "__main__":
log("Service started")
monitor = HDRMonitor()
player = HDRPlayer(monitor)
while not monitor.abortRequested():
if monitor.waitForAbort(1):
break
log("Service stopped")