/
/
/
1"""
2Dataclass models for the Yandex Smart Home API.
3
4Covers device descriptions, capability states, action requests/results,
5callback payloads, and cloud WebSocket messages.
6
7Reference: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/platform-protocol.html
8Reference: https://github.com/dext0r/yandex_smart_home
9"""
10
11from __future__ import annotations
12
13from dataclasses import dataclass, field
14from enum import StrEnum
15from typing import Any
16
17# ---------------------------------------------------------------------------
18# Enums
19# ---------------------------------------------------------------------------
20
21
22class YandexDeviceType(StrEnum):
23 """Yandex Smart Home device types relevant to MA players."""
24
25 MEDIA_DEVICE = "devices.types.media_device"
26 MEDIA_DEVICE_RECEIVER = "devices.types.media_device.receiver"
27
28
29class YandexCapabilityType(StrEnum):
30 """Yandex Smart Home capability types."""
31
32 ON_OFF = "devices.capabilities.on_off"
33 RANGE = "devices.capabilities.range"
34 TOGGLE = "devices.capabilities.toggle"
35 MODE = "devices.capabilities.mode"
36
37
38class YandexRangeInstance(StrEnum):
39 """Range capability instances."""
40
41 VOLUME = "volume"
42 CHANNEL = "channel"
43
44
45class YandexModeInstance(StrEnum):
46 """Mode capability instances."""
47
48 INPUT_SOURCE = "input_source"
49
50
51class YandexToggleInstance(StrEnum):
52 """Toggle capability instances."""
53
54 MUTE = "mute"
55 PAUSE = "pause"
56
57
58class YandexResponseCode(StrEnum):
59 """Yandex Smart Home API response/error codes."""
60
61 DONE = "DONE"
62 DEVICE_UNREACHABLE = "DEVICE_UNREACHABLE"
63 INVALID_ACTION = "INVALID_ACTION"
64 INTERNAL_ERROR = "INTERNAL_ERROR"
65 DEVICE_NOT_FOUND = "DEVICE_NOT_FOUND"
66
67
68# ---------------------------------------------------------------------------
69# Device description â returned by /user/devices
70# ---------------------------------------------------------------------------
71
72
73@dataclass
74class RangeParameters:
75 """Range capability parameters (min/max/precision)."""
76
77 min: float = 0
78 max: float = 100
79 precision: float = 1
80
81
82@dataclass
83class ModeValue:
84 """A single mode value for mode capabilities."""
85
86 value: str
87
88
89@dataclass
90class CapabilityParameters:
91 """Parameters block inside a capability description."""
92
93 instance: str
94 range: RangeParameters | None = None
95 unit: str | None = None
96 random_access: bool | None = None
97 modes: list[ModeValue] | None = None
98
99
100@dataclass
101class CapabilityDescription:
102 """A single capability in a device description."""
103
104 type: str
105 retrievable: bool = True
106 reportable: bool = True
107 parameters: CapabilityParameters | None = None
108
109
110@dataclass
111class YandexDeviceInfo:
112 """Device info block."""
113
114 manufacturer: str = "Music Assistant"
115 model: str = "MA Player"
116 sw_version: str | None = None
117
118
119@dataclass
120class DeviceDescription:
121 """Full device description for /user/devices response."""
122
123 id: str
124 name: str
125 type: str
126 capabilities: list[CapabilityDescription] = field(default_factory=list)
127 device_info: YandexDeviceInfo | None = None
128 room: str | None = None
129 description: str | None = None
130
131
132# ---------------------------------------------------------------------------
133# Capability state â for /user/devices/query and state callbacks
134# ---------------------------------------------------------------------------
135
136
137@dataclass
138class CapabilityInstanceState:
139 """State of a specific capability instance."""
140
141 instance: str
142 value: Any
143
144
145@dataclass
146class CapabilityState:
147 """A capability with its current state."""
148
149 type: str
150 state: CapabilityInstanceState
151
152
153@dataclass
154class DeviceState:
155 """State of a single device (for query or callback)."""
156
157 id: str
158 capabilities: list[CapabilityState] = field(default_factory=list)
159 error_code: str | None = None
160 error_message: str | None = None
161
162
163# ---------------------------------------------------------------------------
164# Action request â from /user/devices/action
165# ---------------------------------------------------------------------------
166
167
168@dataclass
169class CapabilityActionState:
170 """State portion of an action request capability."""
171
172 instance: str
173 value: Any
174 relative: bool = False
175
176
177@dataclass
178class CapabilityAction:
179 """A single capability action from Yandex."""
180
181 type: str
182 state: CapabilityActionState
183
184
185@dataclass
186class DeviceAction:
187 """Action request for a single device."""
188
189 id: str
190 capabilities: list[CapabilityAction] = field(default_factory=list)
191
192
193@dataclass
194class ActionRequestPayload:
195 """Payload of /user/devices/action request."""
196
197 devices: list[DeviceAction] = field(default_factory=list)
198
199
200# ---------------------------------------------------------------------------
201# Action result
202# ---------------------------------------------------------------------------
203
204
205@dataclass
206class ActionResult:
207 """Result of executing a single capability action."""
208
209 status: str = "DONE"
210 error_code: str | None = None
211 error_message: str | None = None
212
213
214@dataclass
215class CapabilityActionResultState:
216 """
217 State with action result for a single capability in an action response.
218
219 Per Yandex Smart Home API, action_result goes inside 'state' alongside instance.
220 """
221
222 instance: str
223 value: Any = None
224 action_result: ActionResult = field(default_factory=ActionResult)
225
226
227@dataclass
228class CapabilityActionResult:
229 """Result for a single capability in an action response."""
230
231 type: str
232 state: CapabilityActionResultState
233
234
235@dataclass
236class DeviceActionResult:
237 """Action results for a single device."""
238
239 id: str
240 capabilities: list[CapabilityActionResult] = field(default_factory=list)
241
242
243# ---------------------------------------------------------------------------
244# Response payloads
245# ---------------------------------------------------------------------------
246
247
248@dataclass
249class DeviceListPayload:
250 """Payload for /user/devices response."""
251
252 user_id: str
253 devices: list[DeviceDescription] = field(default_factory=list)
254
255
256@dataclass
257class DeviceStatesPayload:
258 """Payload for /user/devices/query response."""
259
260 devices: list[DeviceState] = field(default_factory=list)
261
262
263@dataclass
264class ActionResultPayload:
265 """Payload for /user/devices/action response."""
266
267 devices: list[DeviceActionResult] = field(default_factory=list)
268
269
270# ---------------------------------------------------------------------------
271# Callback â state reporting to Yandex
272# ---------------------------------------------------------------------------
273
274
275@dataclass
276class CallbackPayload:
277 """Payload for callback/state POST."""
278
279 user_id: str
280 devices: list[DeviceState] = field(default_factory=list)
281
282
283@dataclass
284class CallbackRequest:
285 """Full callback state request body."""
286
287 ts: float
288 payload: CallbackPayload
289
290
291# ---------------------------------------------------------------------------
292# Cloud WebSocket messages
293# ---------------------------------------------------------------------------
294
295
296@dataclass
297class CloudRequest:
298 """Incoming message from yaha-cloud.ru WebSocket."""
299
300 request_id: str
301 action: str
302 message: dict[str, Any] | None = None
303
304
305@dataclass
306class CloudResponse:
307 """Outgoing response to yaha-cloud.ru WebSocket."""
308
309 request_id: str
310 payload: dict[str, Any] = field(default_factory=dict)
311