Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
5895b4d
feat(yandex_station): v1.0.0 — Local Glagol WebSocket playback, QR au…
github-actions[bot] Apr 7, 2026
8efe8c4
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
c94ee31
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
64d4fe7
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
8b75b03
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
b7c944f
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
21ae157
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
4b02b1b
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
6fc5fa5
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 7, 2026
017d5f7
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 10, 2026
83c1b55
fix(yandex_station): pin ya-passport-auth==1.0.0, regenerate requirem…
trudenboy Apr 10, 2026
8bcc381
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 11, 2026
2ba1391
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 11, 2026
2323a94
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 11, 2026
4622d08
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 12, 2026
fcabf2b
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 12, 2026
78e1fcf
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 12, 2026
b439006
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 13, 2026
f80f250
chore: revert requirements_all.txt to upstream (auto-generated)
trudenboy Apr 13, 2026
706f2dc
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 13, 2026
147492f
chore: reset requirements_all.txt to upstream base (will be regenerat…
trudenboy Apr 13, 2026
41c9840
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 13, 2026
9eca453
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 13, 2026
7882777
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 13, 2026
492bd5c
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 13, 2026
58e8142
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 15, 2026
55dc4cc
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 15, 2026
3633105
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 16, 2026
3635670
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 16, 2026
d98c3bf
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 16, 2026
8a156b3
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 17, 2026
e6c3ca5
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 17, 2026
812db25
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 20, 2026
90704a3
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 20, 2026
5763f8b
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 20, 2026
1188f00
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 20, 2026
7d7aeb3
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 21, 2026
9b941c9
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 21, 2026
f3763f7
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 22, 2026
a512e02
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 22, 2026
ae2d401
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 22, 2026
512438a
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 23, 2026
63f13f7
Merge branch 'dev' into upstream/yandex_station
MarvinSchenkel Apr 23, 2026
e003d2a
Change ya-passport-auth requirement version to exact
trudenboy Apr 23, 2026
8afaa2d
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 23, 2026
ed01a7d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 23, 2026
cd4566a
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 28, 2026
79d3102
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 28, 2026
690c1c4
Update requirements_all.txt
trudenboy Apr 28, 2026
7e526ef
Change ya-passport-auth requirement to exact version
trudenboy Apr 28, 2026
cae6698
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 28, 2026
6eaf4ff
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 28, 2026
6b5d348
Merge branch 'dev' into upstream/yandex_station
trudenboy Apr 28, 2026
9173e93
Update ya-passport-auth requirement version
trudenboy Apr 28, 2026
87b69c5
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 28, 2026
42c1b2c
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] Apr 28, 2026
c52b2f8
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
99f9fd2
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
7384fba
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
7b6643d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
032f172
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
68c49c1
Merge branch 'dev' into upstream/yandex_station
trudenboy May 4, 2026
c82835d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
86fdfbc
Merge branch 'dev' into upstream/yandex_station
trudenboy May 4, 2026
cc2fe7d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
37d71df
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
b4fc5e1
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
f9c25ac
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
09dd591
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
8819c8f
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
abe16c0
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
036cdcc
Merge branch 'dev' into upstream/yandex_station
trudenboy May 4, 2026
bc0913c
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 4, 2026
353001d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
d8e87b9
Merge branch 'dev' into upstream/yandex_station
trudenboy May 5, 2026
3032a37
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
c175594
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
17f0d6d
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
889ddaa
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
cc1994e
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
da3c9a1
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 5, 2026
139e22e
Merge branch 'dev' into upstream/yandex_station
trudenboy May 7, 2026
5fe6edb
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 7, 2026
25a76ba
feat(yandex_station): sync provider from ma-provider-yandex-station v…
github-actions[bot] May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions music_assistant/providers/yandex_station/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
"""Yandex Station Player Provider for Music Assistant.

Play music on Yandex Station smart speakers via local Glagol WebSocket protocol.
Adapted from AlexxIT/YandexStation (MIT license).
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, cast

from music_assistant_models.config_entries import ConfigEntry
from music_assistant_models.enums import ConfigEntryType, ProviderFeature
from music_assistant_models.errors import LoginFailed

from .constants import (
CONF_ACTION_AUTH_COOKIES,
CONF_ACTION_AUTH_QR,
CONF_ACTION_CLEAR_AUTH,
CONF_COOKIES,
CONF_MUSIC_TOKEN,
CONF_X_TOKEN,
)
from .provider import YandexStationProvider
from .yandex_auth import login_with_cookies, perform_qr_auth

if TYPE_CHECKING:
from music_assistant_models.config_entries import ConfigValueType, ProviderConfig
from music_assistant_models.provider import ProviderManifest

from music_assistant.mass import MusicAssistant
from music_assistant.models import ProviderInstanceType

_LOGGER = logging.getLogger(__name__)

SUPPORTED_FEATURES: set[ProviderFeature] = set()


async def get_config_entries(
mass: MusicAssistant,
instance_id: str | None = None, # noqa: ARG001
action: str | None = None,
values: dict[str, ConfigValueType] | None = None,
) -> tuple[ConfigEntry, ...]:
"""Return Config entries to setup this provider."""
if values is None:
values = {}

# Handle QR auth action
if action == CONF_ACTION_AUTH_QR:
session_id = values.get("session_id")
if not session_id:
raise LoginFailed("Missing session_id for QR authentication")
x_token, music_token = await perform_qr_auth(mass, str(session_id))
values[CONF_X_TOKEN] = x_token
values[CONF_MUSIC_TOKEN] = music_token

# Handle cookies auth action
if action == CONF_ACTION_AUTH_COOKIES:
cookies_val = values.get(CONF_COOKIES)
if not cookies_val:
raise LoginFailed("Cookies field is empty")
x_token, music_token = await login_with_cookies(str(cookies_val))
values[CONF_X_TOKEN] = x_token
values[CONF_MUSIC_TOKEN] = music_token
values[CONF_COOKIES] = None # don't persist raw cookies

# Handle clear auth action
if action == CONF_ACTION_CLEAR_AUTH:
values[CONF_X_TOKEN] = None
values[CONF_MUSIC_TOKEN] = None

is_authenticated = bool(values.get(CONF_X_TOKEN))

# Dynamic label text
if not is_authenticated:
label_text = (
"Scan a QR code with the Yandex app on your phone to authenticate.\n\n"
"Alternatively, you can enter tokens manually in the advanced settings."
)
elif action == CONF_ACTION_AUTH_QR:
label_text = "✅ Authenticated! Don't forget to save to complete setup."
else:
label_text = "✅ Authenticated to Yandex."

return (
# Status label
ConfigEntry(
key="label_text",
type=ConfigEntryType.LABEL,
label=label_text,
),
# QR authentication (primary)
ConfigEntry(
key=CONF_ACTION_AUTH_QR,
type=ConfigEntryType.ACTION,
label="Login with QR code",
description="Opens a QR code page — scan it with the Yandex app on your phone.",
action=CONF_ACTION_AUTH_QR,
action_label="Login with QR code",
hidden=is_authenticated,
),
# Cookies authentication (advanced fallback)
ConfigEntry(
key=CONF_COOKIES,
type=ConfigEntryType.SECURE_STRING,
label="Browser Cookies",
description=(
"Open passport.yandex.ru/profile in your browser, "
'use "Copy Cookies" extension to copy cookies, paste here. '
"Supports JSON array or raw cookie string."
),
required=False,
hidden=is_authenticated,
advanced=True,
value="",
),
ConfigEntry(
key=CONF_ACTION_AUTH_COOKIES,
type=ConfigEntryType.ACTION,
label="Login with Cookies",
description="Authenticate using browser cookies from passport.yandex.ru.",
action=CONF_ACTION_AUTH_COOKIES,
action_label="Login with Cookies",
hidden=is_authenticated,
advanced=True,
),
# Clear auth
ConfigEntry(
key=CONF_ACTION_CLEAR_AUTH,
type=ConfigEntryType.ACTION,
label="Reset authentication",
description="Clear the current authentication details.",
action=CONF_ACTION_CLEAR_AUTH,
action_label="Reset authentication",
hidden=not is_authenticated,
),
# x_token (internal storage)
ConfigEntry(
key=CONF_X_TOKEN,
type=ConfigEntryType.SECURE_STRING,
label="Yandex X-Token",
description="Long-lived auth token (~1 year). Auto-obtained via QR login.",
required=True,
hidden=is_authenticated,
advanced=True,
value=cast("str", values.get(CONF_X_TOKEN)) if values else None,
),
# music_token (internal storage)
ConfigEntry(
key=CONF_MUSIC_TOKEN,
type=ConfigEntryType.SECURE_STRING,
label="Yandex Music Token",
description="Auto-obtained from X-Token. No manual entry needed.",
required=False,
hidden=True,
value=cast("str", values.get(CONF_MUSIC_TOKEN)) if values else None,
),
)


async def setup(
mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
x_token = config.get_value(CONF_X_TOKEN)
if not x_token:
msg = "Authentication required. Please login with your Yandex credentials."
raise LoginFailed(msg)
return YandexStationProvider(mass, manifest, config, SUPPORTED_FEATURES)
40 changes: 40 additions & 0 deletions music_assistant/providers/yandex_station/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Constants for Yandex Station Player provider."""

from __future__ import annotations

DOMAIN = "yandex_station"

# Config entry keys
CONF_X_TOKEN = "x_token"
CONF_MUSIC_TOKEN = "music_token"
CONF_COOKIE = "cookie"
CONF_COOKIES = "cookies"

# Config action keys
CONF_ACTION_AUTH_QR = "action_auth_qr"
CONF_ACTION_AUTH_COOKIES = "action_auth_cookies"
CONF_ACTION_CLEAR_AUTH = "action_clear_auth"
CONF_VOICE_CONTROL = "voice_control"

# Yandex API endpoints
GLAGOL_TOKEN_URL = "https://quasar.yandex.net/glagol/token"
QUASAR_DEVICES_URL = "https://iot.quasar.yandex.ru/m/v3/user/devices"
QUASAR_IOT_URL = "https://iot.quasar.yandex.ru"
MUSIC_TOKEN_URL = "https://oauth.mobile.yandex.net/1/token"
PASSPORT_API_URL = "https://mobileproxy.passport.yandex.net"

# Music token OAuth credentials (from yandex-music-api)
MUSIC_CLIENT_ID = "23cabbbdc6cd418abb4b39c32c41195d"
MUSIC_CLIENT_SECRET = "53bc75238f0c4d08a118e51fe9203300"

Comment thread
trudenboy marked this conversation as resolved.
Outdated
# mDNS service type for Yandex Station devices
MDNS_TYPE = "_yandexio._tcp.local."

# WebSocket settings
WS_HEARTBEAT = 55
WS_RECONNECT_BASE_DELAY = 30
WS_RECONNECT_MAX_MULTIPLIER = 10
WS_COMMAND_TIMEOUT = 5

# DDoS protection: minimum interval between Yandex API requests (seconds)
API_REQUEST_INTERVAL = 0.2
Loading
Loading