Skip to content

Add Yandex Smart Home plugin provider#3615

Merged
MarvinSchenkel merged 64 commits intomusic-assistant:devfrom
trudenboy:upstream/yandex_smarthome
Apr 20, 2026
Merged

Add Yandex Smart Home plugin provider#3615
MarvinSchenkel merged 64 commits intomusic-assistant:devfrom
trudenboy:upstream/yandex_smarthome

Conversation

@trudenboy
Copy link
Copy Markdown
Contributor

@trudenboy trudenboy commented Apr 8, 2026

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

Alice voice command -> Yandex Cloud -> Smart Home API -> this plugin -> MA Player commands
                                                                     <- MA Player state -> Yandex state reports

Features

  • Registers MA players as media_device in Yandex Smart Home
  • Capabilities: on/off, volume (absolute and relative), mute, pause, input source selection
  • State reporting: real-time player state sync to Yandex via callback API (batched with 1s debounce)
  • Group player support: group volume/mute via MA group APIs, child-to-group event propagation
  • Three connection modes:
    • Cloud — simple setup with a shared cloud connection token via WebSocket relay at yaha-cloud.ru
    • Cloud Plus — private Yandex Dialogs skill with OAuth authentication via the same relay (for advanced users)
    • Direct — self-hosted HTTP endpoint with built-in OAuth flow, no relay dependency (requires port forwarding or reverse proxy)
  • Player filter — choose which MA players to expose
  • Custom instance naming for Yandex Smart Home
  • Player name normalization for Yandex compatibility (Russian/English only)

Security

  • secrets.compare_digest for all token/secret comparisons (bearer, OAuth client_secret, refresh_token)
  • html.escape + strict domain validation (social.yandex.net) for OAuth redirect URIs
  • Per-install auto-generated client_secret (no hardcoded secrets)
  • SecretStr wrapping for all sensitive tokens (cloud token, access token, client secret)
  • DoS protection: MAX_PENDING_CODES=20 cap on OAuth authorization codes (HTTP 429)
  • JSON parse errors return HTTP 400 (not 500) in direct mode

Provider files (11 modules)

File Purpose
__init__.py Config flow with 3 connection types, setup, action handlers
plugin.py Main plugin class, event subscriptions, request routing
device.py MA Player to Yandex device mapping (capabilities, state, actions)
handlers.py Smart Home API request handlers (discovery, query, action)
notifier.py Batched state change notifications with re-queue on failure, child-to-group propagation
cloud.py WebSocket client for yaha-cloud.ru relay with reconnect backoff
direct.py Direct HTTP connection handler with built-in OAuth flow
constants.py API constants (device types, capabilities, URLs)
schema.py Pydantic-style schema enums for Smart Home protocol
manifest.json Provider metadata (type: plugin, domain: yandex_smarthome, stage: beta)
icon.svg Provider icon

Tests (177 tests across 7 files)

File Tests Coverage
test_basic.py 5 Manifest validation, provider setup
test_device.py 61 Device mapping, state conversion, action execution, groups
test_handlers.py 18 API request/response handling, defensive parsing
test_cloud.py 12 WebSocket connection, message parsing, error responses
test_notifier.py 19 State notification delivery, re-queue on failure, child-to-group
test_schema.py 21 Schema enum validation
test_direct.py 41 OAuth flow, token management, API routing, security

Dependencies

Related PRs

References

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

🔒 Dependency Security Report

📦 Modified Dependencies

music_assistant/providers/yandex_smarthome/manifest.json

Added:

The following dependencies were added or modified:

diff --git a/requirements_all.txt b/requirements_all.txt
index a452ebd9..8de605f3 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -87,6 +87,7 @@ unidecode==1.4.0
 uv>=0.8.0
 websocket-client==1.9.0
 xmltodict==1.0.4
+ya-passport-auth==1.2.3
 yandex-music==2.2.0
 ytmusicapi==1.11.5
 zeroconf==0.148.0

New/modified packages to review:

  • ya-passport-auth==1.2.3

🔍 Vulnerability Scan Results

ERROR:pip_audit._virtual_env:internal pip failure: ERROR: Ignored the following versions that require a different python version: 0.10.0 Requires-Python >=3.10,<3.13; 0.9.0 Requires-Python >=3.8,<3.12; 0.9.1 Requires-Python >=3.8,<3.12
ERROR: Could not find a version that satisfies the requirement audible==0.10.0 (from versions: 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.2.0, 0.2.1, 0.2.2, 0.2.3, 0.3.0, 0.3.1, 0.4.0, 0.4.1, 0.4.2, 0.4.3, 0.4.4, 0.5.0, 0.5.1, 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.6.0, 0.7.0, 0.7.1, 0.7.2, 0.8.0, 0.8.1, 0.8.2)
ERROR: No matching distribution found for audible==0.10.0

ERROR:pip_audit._cli:Failed to install packages: ['/tmp/tmp5ok8_jea/bin/python', '-m', 'pip', 'install', '--no-input', '--keyring-provider=subprocess', '--dry-run', '--report', '/tmp/tmpbvz82s25/tmpojamopto', '-r', 'requirements_all.txt']

⚠️ Vulnerabilities detected! Please review the findings above.


Automated Security Checks

  • Vulnerability Scan: Failed - Known vulnerabilities detected
  • Trusted Sources: All packages have verified source repositories
  • Typosquatting Check: No suspicious package names detected
  • License Compatibility: All licenses are OSI-approved and compatible
  • Supply Chain Risk: Passed - packages appear mature and maintained

Manual Review

Maintainer approval required:

  • I have reviewed the changes above and approve these dependency updates

To approve: Comment /approve-dependencies or manually add the dependencies-reviewed label.

@trudenboy trudenboy changed the title feat(yandex_smarthome): add yandex_smarthome provider v1.0.0 feat: add Yandex Smart Home plugin provider Apr 8, 2026
@trudenboy trudenboy changed the title feat: add Yandex Smart Home plugin provider Add Yandex Smart Home plugin provider Apr 8, 2026
@trudenboy trudenboy marked this pull request as ready for review April 8, 2026 18:54
Copilot AI review requested due to automatic review settings April 8, 2026 18:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread music_assistant/providers/yandex_smarthome/device.py Outdated
Comment thread music_assistant/providers/yandex_smarthome/plugin.py
Comment thread music_assistant/providers/yandex_smarthome/notifier.py
Comment thread music_assistant/providers/yandex_smarthome/schema.py
@trudenboy
Copy link
Copy Markdown
Contributor Author

All 4 review comments addressed in commit 2129580:

  1. device.py source_list typing — Fixed. Updated type annotations from list[str] to list[PlayerSource] across _get_source_list, _build_source_modes, _source_to_mode, _mode_to_source. Replaced getattr(source, 'name', str(source)) with direct source.name access.

  2. plugin.py Cloud Plus validation — Fixed. Moved credential validation (skill_id/skill_token) before creating the CloudManager WebSocket task to prevent orphan background tasks.

  3. notifier.py initial report handle — Fixed. The call_later TimerHandle is now stored in _initial_report_handle and cancelled in stop() to prevent callbacks after unload.

  4. schema.py StrEnum fallback — Kept with updated comment. The fallback is needed because the standalone plugin repo runs local tests on Python 3.9, while upstream requires >=3.12. The try/except pattern ensures compatibility in both contexts.

Copilot AI review requested due to automatic review settings April 9, 2026 14:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Comment thread music_assistant/providers/yandex_smarthome/device.py
Comment thread music_assistant/providers/yandex_smarthome/handlers.py Outdated
Comment thread music_assistant/providers/yandex_smarthome/plugin.py
Comment thread music_assistant/providers/yandex_smarthome/plugin.py
@trudenboy
Copy link
Copy Markdown
Contributor Author

All 4 new review comments addressed in commit 0e6de95:

  1. device.py on_off state — Fixed. get_device_state now sets on=True only when playback_state is PLAYING or PAUSED; idle/stopped → on=False. Matches the action handler semantics (on→play, off→stop).

  2. handlers.py exposed_ids filter — Fixed. handle_devices_query and handle_devices_action now accept exposed_ids param and enforce is_player_exposable check. Unexposed device requests return DEVICE_UNREACHABLE error.

  3. plugin.py consistent user_id — Fixed. Computed self._user_id = self._cloud_instance_id or self._instance_name once in _start_cloud_mode and use it in both API responses and state notifier callbacks.

  4. plugin.py cloud password validation — Fixed. Basic cloud mode now validates cloud_instance_password before starting and fails fast with a clear log message if missing.

Copilot AI review requested due to automatic review settings April 10, 2026 21:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 3 comments.

Comment thread tests/providers/yandex_smarthome/test_direct.py
Comment thread music_assistant/providers/yandex_smarthome/cloud.py
Comment thread music_assistant/providers/yandex_smarthome/plugin.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 1 comment.

Comment thread music_assistant/providers/yandex_smarthome/cloud.py Outdated
Copilot AI review requested due to automatic review settings April 15, 2026 13:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 STRING config 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 standard STRING config value.
"""

Comment thread music_assistant/providers/yandex_smarthome/notifier.py Outdated
Comment thread music_assistant/providers/yandex_smarthome/direct.py Outdated
Comment thread music_assistant/providers/yandex_smarthome/plugin.py Outdated
Copilot AI review requested due to automatic review settings April 16, 2026 08:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 5 comments.

Comment thread music_assistant/providers/yandex_smarthome/direct.py
Comment thread music_assistant/providers/yandex_smarthome/handlers.py Outdated
Comment thread music_assistant/providers/yandex_smarthome/device.py
Comment thread music_assistant/providers/yandex_smarthome/device.py
Comment thread music_assistant/providers/yandex_smarthome/device.py
Copilot AI review requested due to automatic review settings April 17, 2026 12:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.

Comment thread music_assistant/providers/yandex_smarthome/cloud.py
Comment thread music_assistant/providers/yandex_smarthome/device.py
Copilot AI review requested due to automatic review settings April 20, 2026 10:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 1 comment.

Comment on lines +387 to +391
default_value=(
cast("str", values.get(CONF_DIRECT_CLIENT_SECRET))
if values and values.get(CONF_DIRECT_CLIENT_SECRET)
else uuid.uuid4().hex
),
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Suggested change
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,

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@MarvinSchenkel MarvinSchenkel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @trudenboy , looks neat!

@MarvinSchenkel MarvinSchenkel merged commit afa6ecc into music-assistant:dev Apr 20, 2026
10 of 15 checks passed
@OzGav
Copy link
Copy Markdown
Contributor

OzGav commented Apr 21, 2026

@trudenboy can you raise a PR against the beta docs for this plugin?

@trudenboy
Copy link
Copy Markdown
Contributor Author

Done — docs PR raised against beta: music-assistant/music-assistant.io#623

MarvinSchenkel pushed a commit that referenced this pull request Apr 23, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants