Production-readiness pass: security hardening, performance improvements, new services (send_message, set_repeat, refresh_library), diagnostics, reauth flow, image proxy, per-instance device IDs, exponential WS reconnect backoff, ID validation, stale device cleanup, and supporting integration plumbing. Three rounds of independent code review applied. See RELEASE_NOTES.md for the full changelog.
This commit is contained in:
@@ -1,8 +1,157 @@
|
||||
## v0.2.0 (2026-05-26)
|
||||
|
||||
Production-readiness pass: security hardening, performance improvements,
|
||||
new services, and supporting integration plumbing. Three rounds of
|
||||
independent code review applied.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- `EmbyApiClient` and `EmbyWebSocket` constructors now require an injected
|
||||
`aiohttp.ClientSession` and a `device_id`. Both clients never own or close
|
||||
the session — Home Assistant owns it. Custom integrations or scripts
|
||||
importing these classes directly must update their call sites.
|
||||
|
||||
### Features
|
||||
|
||||
- **WebSocket real-time updates** now drive entity state without firing a
|
||||
REST refresh on every `PlaybackProgress` event. Sessions update in place
|
||||
via `dataclasses.replace`; REST falls back to a 5-minute safety net while
|
||||
the WS is up.
|
||||
- **Image proxy**: artwork is fetched server-side with the API key in the
|
||||
`X-Emby-Token` header; the API key never appears in URLs returned to the
|
||||
browser.
|
||||
- **Per-instance device ID** derived from `instance_id.async_get(hass)` and
|
||||
the config-entry id — multiple Home Assistant installs no longer collide
|
||||
on the same Emby server.
|
||||
- **Self-signed HTTPS support** via a new `verify_ssl` toggle in the config
|
||||
flow (defaults to verifying).
|
||||
- **Reauth flow**: when the server rejects the API key, Home Assistant
|
||||
prompts for a new one inline instead of leaving the integration broken.
|
||||
- **Repeat mode control** wired through the standard
|
||||
`media_player.repeat_set` UI; HA `MediaPlayerEnqueue` mapped explicitly to
|
||||
Emby `PlayNow` / `PlayNext` / `PlayLast`.
|
||||
- **Per-client device class** inferred from the Emby client name (AndroidTV /
|
||||
Kodi / Roku → TV, music clients → Speaker, others → generic).
|
||||
- **Three new services**:
|
||||
- `emby_player.send_message` — display a banner on an Emby client
|
||||
(doorbells, alarms, laundry timers).
|
||||
- `emby_player.set_repeat` — set RepeatNone / RepeatOne / RepeatAll.
|
||||
- `emby_player.refresh_library` — trigger a server-side library scan.
|
||||
- **Diagnostics**: redacted entry + sessions JSON dump from
|
||||
Settings → Integrations → "..." → Download diagnostics. API key is
|
||||
redacted; session / device / user IDs are replaced with stable hashes.
|
||||
- **Hub device** registered for `via_device` linkage; per-session devices
|
||||
now appear under it.
|
||||
- **Zeroconf + SSDP discovery hints** so Home Assistant can find Emby
|
||||
servers.
|
||||
- **Stale device cleanup** removes devices for sessions absent over 30
|
||||
minutes (with a 10-minute setup grace period); the coordinator's
|
||||
`forget_session` helper prevents the last-seen map from growing
|
||||
unbounded.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- WebSocket reconnect uses **exponential backoff with jitter** (capped at
|
||||
5 min) instead of a fixed 30 s; auth failures no longer trigger infinite
|
||||
retry; the reconnect task is tracked and cancelled cleanly on unload.
|
||||
- `media_position_updated_at` now reflects the real coordinator update
|
||||
time, eliminating spurious state writes from returning `utcnow()` on
|
||||
every property read.
|
||||
- Authentication failures during a coordinator update now raise
|
||||
`ConfigEntryAuthFailed` so Home Assistant actually triggers the reauth
|
||||
flow.
|
||||
- WebSocket authentication moved into HTTP headers — the API key no longer
|
||||
appears in proxy access logs.
|
||||
- `MediaPlayerDeviceClass.TV` is no longer hardcoded for non-TV clients.
|
||||
- `_attr_has_entity_name = True` combined with `_attr_name` no longer
|
||||
double-prints the device name in the UI.
|
||||
- Browse media now raises `BrowseError` with context (and wraps unexpected
|
||||
exceptions instead of surfacing 500s).
|
||||
- Non-admin API keys fall back to `/Users/Public` instead of failing setup.
|
||||
- All session / item / user IDs are validated against
|
||||
`^[A-Za-z0-9_-]{1,128}$` before interpolation into REST paths
|
||||
(defense-in-depth against path traversal / SSRF).
|
||||
- `image_type` validated against a whitelist (`Primary`, `Backdrop`,
|
||||
`Thumb`, `Logo`, `Banner`, `Art`, `Disc`, `Box`).
|
||||
- `play_media` raises `ServiceValidationError` on bad input (not
|
||||
`ValueError`); validates `media_id` format and `position` type.
|
||||
- `async_remove_config_entry_device` refuses to remove the hub device and
|
||||
any session still present in the coordinator.
|
||||
- WebSocket `ForceKeepAlive` is now echoed as `KeepAlive` so the server
|
||||
doesn't drop idle connections.
|
||||
- `PlaySessionId` fallback removed from playback-event parsing — only
|
||||
`SessionId` is matched, eliminating cross-device false positives.
|
||||
- `_safe_int` helper hardens numeric field parsing against `null` /
|
||||
string / malformed payloads (`RunTimeTicks`, `PositionTicks`,
|
||||
`VolumeLevel`, etc.).
|
||||
- Image fetches get a dedicated 30 s timeout, separate from the 15 s REST
|
||||
default.
|
||||
- `manifest.json` `codeowners` corrected to an empty list (hassfest
|
||||
validates entries against GitHub handles).
|
||||
|
||||
### Performance
|
||||
|
||||
- WebSocket / REST race resolved: `_async_update_data` records
|
||||
`request_started`, then merges REST results with any session whose
|
||||
`last_seen` is newer (WS state wins for in-flight progress).
|
||||
- REST poll interval automatically slows to 5 min while the WebSocket is
|
||||
connected, restoring the user-configured interval if it disconnects.
|
||||
- WebSocket callbacks may be sync or async; async ones are detached via
|
||||
`asyncio.create_task` so a slow consumer can't stall the reader.
|
||||
|
||||
---
|
||||
|
||||
### Development / Internal
|
||||
|
||||
- Frozen dataclasses (`EmbyNowPlaying`, `EmbyPlayState`, `EmbySession`)
|
||||
across the coordinator state — safer for concurrency and immutability.
|
||||
- `manifest.json` declares `integration_type: hub`, `quality_scale: silver`,
|
||||
`loggers`, and discovery hints.
|
||||
- HACS minimum Home Assistant bumped to `2024.10.0`.
|
||||
- Client version sourced from `manifest.json` at startup via
|
||||
`loader.async_get_integration` (no more `DEVICE_VERSION` drift).
|
||||
- New `diagnostics.py`, `services.py`, `services.yaml`.
|
||||
- Strings + `translations/en.json` extended for `verify_ssl`,
|
||||
`reauth_confirm`, and the three new services.
|
||||
- `README.md` and `CLAUDE.md` rewritten to match the v0.2.0 state of the
|
||||
integration.
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Files Changed</summary>
|
||||
|
||||
| File | Status |
|
||||
|------|--------|
|
||||
| `CLAUDE.md` | Modified |
|
||||
| `README.md` | Modified |
|
||||
| `RELEASE_NOTES.md` | Modified |
|
||||
| `hacs.json` | Modified |
|
||||
| `custom_components/emby_player/__init__.py` | Modified |
|
||||
| `custom_components/emby_player/api.py` | Modified |
|
||||
| `custom_components/emby_player/browse_media.py` | Modified |
|
||||
| `custom_components/emby_player/config_flow.py` | Modified |
|
||||
| `custom_components/emby_player/const.py` | Modified |
|
||||
| `custom_components/emby_player/coordinator.py` | Modified |
|
||||
| `custom_components/emby_player/manifest.json` | Modified |
|
||||
| `custom_components/emby_player/media_player.py` | Modified |
|
||||
| `custom_components/emby_player/strings.json` | Modified |
|
||||
| `custom_components/emby_player/translations/en.json` | Modified |
|
||||
| `custom_components/emby_player/websocket.py` | Modified |
|
||||
| `custom_components/emby_player/diagnostics.py` | Added |
|
||||
| `custom_components/emby_player/services.py` | Added |
|
||||
| `custom_components/emby_player/services.yaml` | Added |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## v0.1.0 (2026-03-26)
|
||||
|
||||
Initial release of the **Emby Media Player** custom integration for Home Assistant (HACS).
|
||||
|
||||
### Features
|
||||
|
||||
- Full Emby Server media player integration for Home Assistant ([46cb2fb](https://git.dolgolyov-family.by/alexei.dolgolyov/haos-hacs-emby-media-player/commit/46cb2fb))
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user