Skip to content

Commit 6d11583

Browse files
committed
Переход на API v4 для управления сценариями (#53)
1 parent b0107e7 commit 6d11583

File tree

3 files changed

+122
-77
lines changed

3 files changed

+122
-77
lines changed

custom_components/yandex_station_intents/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
248248

249249

250250
async def _async_setup_intents(
251-
intents: list[Intent], quasar: YandexQuasar, target_device: Device | None = None
251+
intents: list[Intent], quasar: YandexQuasar, intent_player_device: Device | None = None
252252
) -> None:
253253
await quasar.delete_stale_intents(intents)
254254

@@ -260,7 +260,9 @@ async def _async_setup_intents(
260260

261261
try:
262262
await quasar.async_add_or_update_intent(
263-
intent=item, intent_quasar_id=quasar_intents.get(item.name), target_device=target_device
263+
intent=item,
264+
intent_quasar_id=quasar_intents.get(item.name),
265+
intent_player_device=intent_player_device,
264266
)
265267
except AuthException:
266268
_LOGGER.exception(

custom_components/yandex_station_intents/yandex_intent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def scenario_name(self) -> str:
4040
return f"{INTENT_ID_MARKER} {self.name}"
4141

4242
@property
43-
def scenario_step_value(self) -> str:
43+
def scenario_text_command(self) -> str:
4444
rv = STATION_STUB_COMMAND
4545
if self.say_phrase and not self.execute_command:
4646
rv = self.say_phrase

custom_components/yandex_station_intents/yandex_quasar.py

Lines changed: 117 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from abc import ABC, abstractmethod
34
from dataclasses import dataclass
45
import json
56
import logging
@@ -21,6 +22,7 @@
2122

2223
URL_USER = "https://iot.quasar.yandex.ru/m/user"
2324
URL_V3_USER = "https://iot.quasar.yandex.ru/m/v3/user"
25+
URL_V4_USER = "https://iot.quasar.yandex.ru/m/v4/user"
2426
DEFAULT_RECONNECTION_DELAY = 2
2527
MAX_RECONNECTION_DELAY = 180
2628

@@ -44,76 +46,124 @@ def from_dict(cls, data: ConfigType) -> Device:
4446
)
4547

4648

47-
class ScenarioStep:
48-
def __init__(self, value: str | None = None, launch_devices: list[Any] | None = None) -> None:
49-
self._value = value
50-
self._launch_devices = launch_devices or []
51-
self._request_speaker_capabilities: list[dict[Any, Any]] = []
49+
class ScenarioStepItem(ABC):
50+
"""
51+
Действие в сценариях.
52+
"""
53+
54+
@property
55+
@abstractmethod
56+
def as_dict(self) -> ConfigType:
57+
pass
58+
59+
60+
class ScenarioStepItemDeviceChannel(ScenarioStepItem):
61+
"""
62+
Действие "Канал X" для служебного плеера.
63+
"""
64+
65+
def __init__(self, device: Device, channel: int):
66+
self._device = device
67+
self._channel = channel
5268

5369
@property
5470
def as_dict(self) -> ConfigType:
5571
return {
56-
"type": "scenarios.steps.actions",
57-
"parameters": {
58-
"launch_devices": self._launch_devices,
59-
"requested_speaker_capabilities": self._request_speaker_capabilities,
72+
"id": self._device.id,
73+
"type": "step.action.item.device",
74+
"value": {
75+
"id": self._device.id,
76+
# "item_type": "device",
77+
"capabilities": [
78+
{
79+
"type": "devices.capabilities.range",
80+
"state": {
81+
"instance": "channel",
82+
"value": self._channel,
83+
},
84+
}
85+
],
6086
},
6187
}
6288

6389

64-
class ScenarioStepTTS(ScenarioStep):
90+
class ScenarioStepItemRequestedDevice(ScenarioStepItem, ABC):
6591
"""
66-
Проговаривает текст полностью и только потом выполняет следующий шаг.
67-
В интерфейсе: "Прочитать текст вслух"
68-
В список событий не попадает.
92+
Действия из раздела "Любое умное устройство, которое активирует сценарий"
6993
"""
7094

71-
def __init__(self, value: str, launch_devices: list[Any] | None = None) -> None:
72-
super().__init__(value, launch_devices)
95+
@property
96+
def as_dict(self) -> ConfigType:
97+
return {
98+
"id": "requested-device",
99+
"type": "step.action.item.requested_device_with_assistant",
100+
"value": self._value,
101+
}
73102

74-
self._request_speaker_capabilities.append(
75-
{
76-
"parameters": {"instance": "tts"},
77-
"retrievable": False,
78-
"state": {"instance": "tts", "value": {"text": self._value}},
79-
"type": "devices.capabilities.quasar",
80-
}
81-
)
103+
@property
104+
@abstractmethod
105+
def _value(self) -> ConfigType:
106+
pass
82107

83108

84-
class ScenarioStepTextAction(ScenarioStep):
109+
class ScenarioStepItemRequestedDeviceTTS(ScenarioStepItemRequestedDevice):
85110
"""
86-
Выполняет команду на колонке.
87-
В интерфейсе: "Ответить на вопрос или выполнить команду"
111+
Проговаривает текст на колонке, действие не попадает в список событий.
112+
В интерфейсе: "Прочитать текст вслух"
88113
"""
89114

90-
def __init__(self, value: str, launch_devices: list[Any] | None = None) -> None:
91-
super().__init__(value, launch_devices)
115+
def __init__(self, text: str):
116+
self._text = text
92117

93-
self._request_speaker_capabilities.append(
94-
{
95-
"parameters": {"instance": "text_action"},
96-
"state": {"instance": "text_action", "value": self._value},
97-
"type": "devices.capabilities.quasar.server_action",
98-
}
99-
)
118+
@property
119+
def _value(self) -> ConfigType:
120+
return {
121+
"type": "devices.capabilities.quasar",
122+
"state": {
123+
"instance": "tts",
124+
"value": {"text": self._text},
125+
},
126+
}
100127

101128

102-
class ScenarioStepPhraseAction(ScenarioStep):
129+
class ScenarioStepItemRequestedDeviceTTSPA(ScenarioStepItemRequestedDevice):
103130
"""
104-
Проговаривает текст и сразу выполняет следующую команду.
131+
Проговаривает текст на колонке, действие попадает в список событий.
105132
В интерфейсе: отсутствует
106133
"""
107134

108-
def __init__(self, value: str, launch_devices: list[Any] | None = None) -> None:
109-
super().__init__(value, launch_devices)
135+
def __init__(self, text: str):
136+
self._text = text
110137

111-
self._request_speaker_capabilities.append(
112-
{
113-
"state": {"instance": "phrase_action", "value": self._value},
114-
"type": "devices.capabilities.quasar.server_action",
115-
}
116-
)
138+
@property
139+
def _value(self) -> ConfigType:
140+
return {
141+
"type": "devices.capabilities.quasar.server_action",
142+
"state": {
143+
"instance": "phrase_action",
144+
"value": self._text,
145+
},
146+
}
147+
148+
149+
class ScenarioStepItemRequestedDeviceTextAction(ScenarioStepItemRequestedDevice):
150+
"""
151+
Выполняет команду на колонке.
152+
В интерфейсе: "Ответить на вопрос или выполнить команду"
153+
"""
154+
155+
def __init__(self, command: str):
156+
self._command = command
157+
158+
@property
159+
def _value(self) -> ConfigType:
160+
return {
161+
"type": "devices.capabilities.quasar.server_action",
162+
"state": {
163+
"instance": "text_action",
164+
"value": self._command,
165+
},
166+
}
117167

118168

119169
class YandexQuasar:
@@ -160,48 +210,41 @@ async def async_get_intents(self) -> dict[str, str]:
160210
return rv
161211

162212
async def async_add_or_update_intent(
163-
self, intent: Intent, intent_quasar_id: str | None, target_device: Device | None
213+
self, intent: Intent, intent_quasar_id: str | None, intent_player_device: Device | None
164214
) -> None:
165-
steps: list[ScenarioStep] = []
166-
167-
if target_device:
168-
steps.append(
169-
ScenarioStep(
170-
launch_devices=[
171-
{
172-
"id": target_device.id,
173-
"capabilities": [
174-
{
175-
"type": "devices.capabilities.range",
176-
"state": {"instance": "channel", "relative": False, "value": intent.id},
177-
}
178-
],
179-
}
180-
]
181-
)
182-
)
183-
184-
if intent.say_phrase and intent.execute_command:
185-
steps.append(ScenarioStepTTS(intent.say_phrase))
186-
steps.append(ScenarioStepTextAction(intent.scenario_step_value))
187-
elif intent.say_phrase:
188-
steps.append(ScenarioStepPhraseAction(intent.scenario_step_value))
215+
step_items: list[ScenarioStepItem] = []
216+
217+
if intent_player_device:
218+
if intent.say_phrase:
219+
step_items.append(ScenarioStepItemRequestedDeviceTTS(intent.say_phrase))
220+
step_items.append(ScenarioStepItemDeviceChannel(intent_player_device, intent.id))
189221
else:
190-
steps.append(ScenarioStepTextAction(intent.scenario_step_value))
222+
if intent.say_phrase and intent.execute_command:
223+
step_items.append(ScenarioStepItemRequestedDeviceTTS(intent.say_phrase))
224+
step_items.append(ScenarioStepItemRequestedDeviceTextAction(intent.scenario_text_command))
225+
elif intent.say_phrase:
226+
step_items.append(ScenarioStepItemRequestedDeviceTTSPA(intent.scenario_text_command))
227+
else:
228+
step_items.append(ScenarioStepItemRequestedDeviceTextAction(intent.scenario_text_command))
191229

192230
payload = {
193231
"name": intent.scenario_name,
194232
"icon": "home",
195233
"triggers": [{"type": "scenario.trigger.voice", "value": v} for v in intent.trigger_phrases],
196-
"steps": [s.as_dict for s in steps],
234+
"steps": [
235+
{
236+
"type": "scenarios.steps.actions.v2",
237+
"parameters": {"items": [si.as_dict for si in step_items]},
238+
}
239+
],
197240
}
198241

199242
if intent_quasar_id:
200243
_LOGGER.debug(f"Обновление сценария {intent.scenario_name!r}: {payload}")
201-
r = await self._session.put(f"{URL_USER}/scenarios/{intent_quasar_id}", json=payload)
244+
r = await self._session.put(f"{URL_V4_USER}/scenarios/{intent_quasar_id}", json=payload)
202245
else:
203246
_LOGGER.debug(f"Создание сценария {intent.scenario_name!r}: {payload}")
204-
r = await self._session.post(f"{URL_USER}/scenarios", json=payload)
247+
r = await self._session.post(f"{URL_V4_USER}/scenarios", json=payload)
205248

206249
resp = await r.json()
207250
assert resp["status"] == "ok", resp

0 commit comments

Comments
 (0)