## 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.
---
Files Changed
| 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 |
---
## 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))
---
All Commits
| Hash | Message | Author |
|------|---------|--------|
| [46cb2fb](https://git.dolgolyov-family.by/alexei.dolgolyov/haos-hacs-emby-media-player/commit/46cb2fb) | Initial commit for `Emby Media Player` HAOS HACS integration | alexei.dolgolyov |