feat(client): v0.3.0 server compat — WS subprotocol auth, 429 retry, HTTPS, X-Request-ID
Aligns the integration with the four wire-level changes shipped in
media-server v0.3.0/0.3.1 without breaking back-compat with older
server versions or pre-existing config entries.
- WebSocket auth via Sec-WebSocket-Protocol: media-server.token.<T>
(preferred by server v0.3.0+). The ?token= query is still sent so
older servers and unauthenticated mode both keep working — aiohttp
completes the handshake even when the server doesn't echo the
subprotocol back.
- 429 Too Many Requests surfaced as MediaServerRateLimitError with
Retry-After parsed; execute_script() sleeps min(retry_after, 30)
and retries once before falling through to the caller.
- Optional HTTPS/WSS (CONF_USE_SSL) + optional certificate verification
toggle (CONF_VERIFY_SSL) wired through the config flow, client, and
WebSocket. Defaults preserve http+verified behaviour, so existing
config entries are unchanged.
- X-Request-ID header (uuid4 hex) on every HTTP call so HA-side issues
can be cross-referenced with the server's access/audit logs. The
format matches the server's ^[A-Za-z0-9._-]{1,128}\$ allow-list so
the id is preserved verbatim instead of being replaced server-side.
Bumps manifest version to 0.3.3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,11 +36,15 @@ from .const import (
|
||||
CONF_PORT,
|
||||
CONF_TOKEN,
|
||||
CONF_POLL_INTERVAL,
|
||||
CONF_USE_SSL,
|
||||
CONF_USE_WEBSOCKET,
|
||||
CONF_VERIFY_SSL,
|
||||
DEFAULT_POLL_INTERVAL,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_USE_WEBSOCKET,
|
||||
DEFAULT_RECONNECT_INTERVAL,
|
||||
DEFAULT_USE_SSL,
|
||||
DEFAULT_USE_WEBSOCKET,
|
||||
DEFAULT_VERIFY_SSL,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -87,6 +91,8 @@ async def async_setup_entry(
|
||||
port=entry.data[CONF_PORT],
|
||||
token=entry.data[CONF_TOKEN],
|
||||
use_websocket=use_websocket,
|
||||
use_ssl=entry.data.get(CONF_USE_SSL, DEFAULT_USE_SSL),
|
||||
verify_ssl=entry.data.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL),
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
@@ -124,6 +130,8 @@ class MediaPlayerCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
port: int,
|
||||
token: str,
|
||||
use_websocket: bool = True,
|
||||
use_ssl: bool = DEFAULT_USE_SSL,
|
||||
verify_ssl: bool = DEFAULT_VERIFY_SSL,
|
||||
entry: ConfigEntry | None = None,
|
||||
) -> None:
|
||||
"""Initialize the coordinator.
|
||||
@@ -136,6 +144,8 @@ class MediaPlayerCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
port: Server port
|
||||
token: API token
|
||||
use_websocket: Whether to use WebSocket for updates
|
||||
use_ssl: Talk WSS instead of WS
|
||||
verify_ssl: Verify TLS cert (off for self-signed)
|
||||
entry: Config entry (for integration reload on scripts change)
|
||||
"""
|
||||
super().__init__(
|
||||
@@ -149,6 +159,8 @@ class MediaPlayerCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
self._port = port
|
||||
self._token = token
|
||||
self._use_websocket = use_websocket
|
||||
self._use_ssl = use_ssl
|
||||
self._verify_ssl = verify_ssl
|
||||
self._entry = entry
|
||||
self._ws_client: MediaServerWebSocket | None = None
|
||||
self._ws_connected = False
|
||||
@@ -173,6 +185,8 @@ class MediaPlayerCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
on_disconnect=self._handle_ws_disconnect,
|
||||
on_scripts_changed=self._handle_ws_scripts_changed,
|
||||
on_foreground_update=self._handle_ws_foreground_update,
|
||||
use_ssl=self._use_ssl,
|
||||
verify_ssl=self._verify_ssl,
|
||||
)
|
||||
|
||||
if await self._ws_client.connect():
|
||||
|
||||
Reference in New Issue
Block a user