Add Yandex Smart Home plugin provider#3615
Add Yandex Smart Home plugin provider#3615MarvinSchenkel merged 64 commits intomusic-assistant:devfrom
Conversation
🔒 Dependency Security Report📦 Modified Dependencies
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a new yandex_smarthome plugin provider that exposes Music Assistant players to Yandex Alice / Yandex Smart Home API, including discovery, query/action handling, and state reporting via the yaha-cloud.ru WebSocket relay (Cloud / Cloud Plus).
Changes:
- Added Yandex Smart Home provider implementation (cloud relay client, device mapping, API handlers, state notifier, config flow, manifest/icon, protocol schema).
- Added comprehensive provider test suite covering schema models, handlers, device mapping/actions, cloud WS behavior, and notifier batching.
- Introduced provider-specific constants and configuration entries for Cloud/Cloud Plus setup and exposed-player filtering.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| music_assistant/providers/yandex_smarthome/init.py | Provider setup + config entries/actions (cloud registration + OTP) and Cloud Plus UI guidance. |
| music_assistant/providers/yandex_smarthome/cloud.py | WebSocket relay client and helper functions to register instances / fetch OTP from yaha-cloud.ru. |
| music_assistant/providers/yandex_smarthome/constants.py | Provider constants (config keys, URLs, timing, capability identifiers). |
| music_assistant/providers/yandex_smarthome/device.py | Maps MA Player ↔ Yandex devices/capabilities and executes incoming capability actions. |
| music_assistant/providers/yandex_smarthome/handlers.py | Pure handler functions for discovery/query/action/unlink + response envelope building. |
| music_assistant/providers/yandex_smarthome/notifier.py | Event-driven state reporting with debounce batching + periodic heartbeat/discovery notification. |
| music_assistant/providers/yandex_smarthome/plugin.py | Main PluginProvider wiring: cloud relay request routing + notifier startup/shutdown. |
| music_assistant/providers/yandex_smarthome/schema.py | Dataclass protocol models/enums for Yandex Smart Home and relay messages. |
| music_assistant/providers/yandex_smarthome/manifest.json | Provider metadata registration. |
| music_assistant/providers/yandex_smarthome/icon.svg | Provider icon asset (embedded image). |
| tests/providers/yandex_smarthome/init.py | Declares the provider test package. |
| tests/providers/yandex_smarthome/test_basic.py | Manifest/constants import/shape validation tests. |
| tests/providers/yandex_smarthome/test_cloud.py | CloudManager + registration/OTP helper tests. |
| tests/providers/yandex_smarthome/test_device.py | Device description/state mapping + action execution tests (incl. channel/input_source). |
| tests/providers/yandex_smarthome/test_handlers.py | Handler function tests for list/query/action/unlink + payload parsing/build_response. |
| tests/providers/yandex_smarthome/test_notifier.py | Notifier lifecycle, batching/flush, report-all, and Cloud Plus callback behavior tests. |
| tests/providers/yandex_smarthome/test_schema.py | Schema model/enum value and serialization roundtrip tests. |
|
All 4 review comments addressed in commit 2129580:
|
|
All 4 new review comments addressed in commit 0e6de95:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
music_assistant/providers/yandex_smarthome/init.py:1
- These entries look informational/copy-only (derived URLs / console links), but they are modeled as editable
STRINGconfig fields with normal keys. Depending on how the config system persists values, this can end up storing/round-tripping unused keys and confusing users (they can edit fields that the plugin never reads). If the config framework supports it, use a non-persisted display type (e.g.,LABEL) or a read-only/copy field rather than a standardSTRINGconfig value.
"""
| default_value=( | ||
| cast("str", values.get(CONF_DIRECT_CLIENT_SECRET)) | ||
| if values and values.get(CONF_DIRECT_CLIENT_SECRET) | ||
| else uuid.uuid4().hex | ||
| ), |
There was a problem hiding this comment.
[PROBLEM] The direct-mode direct_client_secret is generated with uuid.uuid4().hex as a default_value when no value is present, so fetching config entries multiple times before the user clicks Save can show different secrets and break OAuth client_secret validation. Generate the secret once per config-flow session by writing it into values (e.g., values.setdefault(CONF_DIRECT_CLIENT_SECRET, uuid.uuid4().hex)) and then use that stored value (preferably via the entry’s value) so it remains stable until persisted.
| default_value=( | |
| cast("str", values.get(CONF_DIRECT_CLIENT_SECRET)) | |
| if values and values.get(CONF_DIRECT_CLIENT_SECRET) | |
| else uuid.uuid4().hex | |
| ), | |
| value=( | |
| cast( | |
| "str", | |
| values.setdefault(CONF_DIRECT_CLIENT_SECRET, uuid.uuid4().hex), | |
| ) | |
| if values is not None | |
| else None | |
| ), | |
| default_value=uuid.uuid4().hex if values is None else None, |
MarvinSchenkel
left a comment
There was a problem hiding this comment.
Thanks @trudenboy , looks neat!
|
@trudenboy can you raise a PR against the beta docs for this plugin? |
|
Done — docs PR raised against |
## Yandex Smart Home — dependency bump Bumps the provider's auth library `ya-passport-auth` from **1.2.3** to **1.3.0**. **Source**: [trudenboy/ma-provider-yandex-smarthome](https://github.com/trudenboy/ma-provider-yandex-smarthome) · tag [v1.3.0](https://github.com/trudenboy/ma-provider-yandex-smarthome/releases/tag/v1.3.0) --- ### Why Upstream `ya-passport-auth` 1.3.0 adds OAuth Device Flow (RFC 8628) as a third login method and the `Credentials.refresh_token` field (backwards compatible, defaults to `None`). - Changelog: https://github.com/trudenboy/ya-passport-auth/blob/main/CHANGELOG.md - PyPI: https://pypi.org/project/ya-passport-auth/1.3.0/ ### Scope of this PR No provider-side code changes. The provider only consumes `SecretStr` from the library, whose contract is unchanged. Runtime constraints are identical: `aiohttp<4,>=3.10`, `yarl>=1.12`, Python ≥3.12. ### Files | File | Change | |------|--------| | `music_assistant/providers/yandex_smarthome/manifest.json` | `ya-passport-auth==1.2.3` → `1.3.0` | | `requirements_all.txt` | `ya-passport-auth==1.2.3` → `1.3.0` | | `tests/providers/yandex_smarthome/test_basic.py` | assertion updated to `==1.3.0` | 3 files changed, 3 insertions(+), 3 deletions(-). --- > Follow-up to #3615. Draft until reviewed. Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Summary
New plugin provider that exposes Music Assistant players to Yandex Alice voice assistant via the Yandex Smart Home API, enabling voice control of MA players through Alice commands.
Architecture
Features
media_devicein Yandex Smart HomeSecurity
secrets.compare_digestfor all token/secret comparisons (bearer, OAuth client_secret, refresh_token)html.escape+ strict domain validation (social.yandex.net) for OAuth redirect URIsclient_secret(no hardcoded secrets)SecretStrwrapping for all sensitive tokens (cloud token, access token, client secret)MAX_PENDING_CODES=20cap on OAuth authorization codes (HTTP 429)Provider files (11 modules)
__init__.pyplugin.pydevice.pyhandlers.pynotifier.pycloud.pydirect.pyconstants.pyschema.pymanifest.jsonicon.svgTests (177 tests across 7 files)
test_basic.pytest_device.pytest_handlers.pytest_cloud.pytest_notifier.pytest_schema.pytest_direct.pyDependencies
ya-passport-auth==1.2.3— Yandex OAuth authenticationRelated PRs
beta): docs(plugins): add Yandex Smart Home plugin page music-assistant.io#623References