docs: добавлен протокол WebSocket команд (set_volume, mute, play_sound, etc.)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-05-20 16:04:33 +03:00
co-authored by Qwen-Coder
parent 6e06120b55
commit b1c6ef3385
+181 -8
View File
@@ -225,10 +225,22 @@ async def start_websocket_server(self) -> None:
self._websocket_clients.add(websocket) self._websocket_clients.add(websocket)
try: try:
# Ждать отключения клиента # Ждать команды от клиента
async for message in websocket: async for message in websocket:
# Игнорировать входящие сообщения (только рассылка) try:
_LOGGER.debug(f"Received from WebSocket: {message}") data = json.loads(message)
command = data.get('command')
command_data = data.get('data', {})
if command:
_LOGGER.debug(f"Received command: {command}")
await self.handle_websocket_command(command, command_data)
else:
_LOGGER.debug(f"Received message (no command): {data}")
except json.JSONDecodeError:
_LOGGER.warning(f"Invalid JSON from WebSocket: {message}")
except Exception as exc:
_LOGGER.error(f"Error processing command: {exc}")
except websockets.exceptions.ConnectionClosed: except websockets.exceptions.ConnectionClosed:
pass # Нормальное отключение pass # Нормальное отключение
except Exception as exc: except Exception as exc:
@@ -270,7 +282,83 @@ async def stop_websocket_server(self) -> None:
_LOGGER.info("WebSocket server stopped") _LOGGER.info("WebSocket server stopped")
``` ```
#### 2.4 Интеграция в жизненный цикл #### 2.4 Обработчик команд
```python
async def handle_websocket_command(self, command: str, data: dict) -> None:
"""
Обработать команду от WebSocket клиента.
Поддерживаемые команды:
- set_volume: Изменить громкость
- mute: Заглушить микрофон
- unmute: Включить микрофон
- play_sound: Воспроизвести WAV файл
- set_led_brightness: Яркость LED
- set_led_color: Цвет LED
- trigger_event: Отправить событие в Home Assistant
"""
import json
try:
if command == "set_volume":
# Изменить громкость (0.0 - 1.0)
level = float(data.get("level", 1.0))
level = max(0.0, min(1.0, level)) # Clamp 0-1
# Сохранить в settings (потребуется добавить mutable settings)
_LOGGER.info(f"Setting volume to {level}")
# Примечание: settings frozen, нужно будет изменить архитектуру
elif command == "mute":
_LOGGER.info("Muting microphone")
self.microphone_muted = True
elif command == "unmute":
_LOGGER.info("Unmuting microphone")
self.microphone_muted = False
elif command == "play_sound":
# Воспроизвести WAV файл
wav_path = data.get("path")
if wav_path:
_LOGGER.info(f"Playing sound: {wav_path}")
await self._play_wav(wav_path, mute_microphone=False)
else:
_LOGGER.warning("play_sound command missing 'path'")
elif command == "set_led_brightness":
# Отправить команду в LED сервис (через event.uri)
brightness = int(data.get("brightness", 128))
_LOGGER.info(f"Setting LED brightness to {brightness}")
# Отправить событие в event service
elif command == "set_led_color":
# Установить цвет LED
r = int(data.get("r", 0))
g = int(data.get("g", 0))
b = int(data.get("b", 0))
_LOGGER.info(f"Setting LED color to RGB({r}, {g}, {b})")
# Отправить событие в event service
elif command == "trigger_event":
# Отправить событие в Home Assistant
event_type = data.get("event_type")
event_data = data.get("data", {})
if event_type:
_LOGGER.info(f"Triggering event: {event_type}")
# Отправить RunPipeline или другое событие
else:
_LOGGER.warning("trigger_event missing 'event_type'")
else:
_LOGGER.warning(f"Unknown command: {command}")
except Exception as exc:
_LOGGER.error(f"Error handling command {command}: {exc}")
```
#### 2.5 Интеграция в жизненный цикл
**В `_start()`:** **В `_start()`:**
@@ -305,7 +393,7 @@ async def _stop(self) -> None:
self.state = State.STOPPED self.state = State.STOPPED
``` ```
#### 2.5 Обновить `forward_event()` #### 2.6 Обновить `forward_event()`
```python ```python
async def forward_event(self, event: Event) -> None: async def forward_event(self, event: Event) -> None:
@@ -328,6 +416,81 @@ async def forward_event(self, event: Event) -> None:
--- ---
### Этап WS-2A: Протокол WebSocket команд (30 мин) ⏳ ОЖИДАЕТСЯ
**Формат сообщений:**
```json
{
"command": "имя_команды",
"data": {
"ключ": "значение"
}
}
```
**Поддерживаемые команды:**
| Команда | Данные | Описание |
|---------|--------|----------|
| `set_volume` | `{"level": 0.0-1.0}` | Изменить громкость |
| `mute` | `{}` | Заглушить микрофон |
| `unmute` | `{}` | Включить микрофон |
| `play_sound` | `{"path": "/path/to.wav"}` | Воспроизвести WAV |
| `set_led_brightness` | `{"brightness": 0-255}` | Яркость LED |
| `set_led_color` | `{"r": 255, "g": 0, "b": 0}` | Цвет LED (RGB) |
| `trigger_event` | `{"event_type": "...", "data": {...}}` | Событие в HA |
**Примеры:**
```javascript
// Установить громкость 70%
ws.send(JSON.stringify({
"command": "set_volume",
"data": {"level": 0.7}
}));
// Заглушить микрофон
ws.send(JSON.stringify({
"command": "mute",
"data": {}
}));
// Воспроизвести звук
ws.send(JSON.stringify({
"command": "play_sound",
"data": {"path": "/sounds/awake.wav"}
}));
// Установить цвет LED (красный)
ws.send(JSON.stringify({
"command": "set_led_color",
"data": {"r": 255, "g": 0, "b": 0}
}));
// Отправить событие в Home Assistant
ws.send(JSON.stringify({
"command": "trigger_event",
"data": {
"event_type": "wyoming_satellite.custom",
"data": {"custom_data": "value"}
}
}));
```
**Ответы сервера:**
Сервер отправляет события в формате:
```json
{
"type": "voice-started",
"data": {}
}
```
---
### Этап WS-3: Модификация `wyoming_satellite/__main__.py` (20 мин) ⏳ ОЖИДАЕТСЯ ### Этап WS-3: Модификация `wyoming_satellite/__main__.py` (20 мин) ⏳ ОЖИДАЕТСЯ
#### 3.1 Добавить CLI аргументы #### 3.1 Добавить CLI аргументы
@@ -497,11 +660,11 @@ python3 -m wyoming_satellite \
| Файл | Тип | Изменения | Строк | | Файл | Тип | Изменения | Строк |
|------|-----|-----------|-------| |------|-----|-----------|-------|
| `wyoming_satellite/settings.py` | Изменён | EventSettings + WebSocket | ~10 | | `wyoming_satellite/settings.py` | Изменён | EventSettings + WebSocket | ~10 |
| `wyoming_satellite/satellite.py` | Изменён | WebSocket server + broadcast | ~150 | | `wyoming_satellite/satellite.py` | Изменён | WebSocket server + broadcast + commands | ~200 |
| `wyoming_satellite/__main__.py` | Изменён | CLI аргументы | ~20 | | `wyoming_satellite/__main__.py` | Изменён | CLI аргументы | ~20 |
| `examples/websocket-service/*` | Существующий | Готовые клиенты | — | | `examples/websocket-service/*` | Существующий | Готовые клиенты | — |
| `docs/integration/mqtt-led.md` | Изменён | Документация WebSocket | ~50 | | `docs/integration/mqtt-led.md` | Изменён | Документация WebSocket + commands | ~80 |
| **ВСЕГО** | | | **~230 строк** | | **ВСЕГО** | | | **~310 строк** |
--- ---
@@ -564,6 +727,16 @@ sudo systemctl restart wyoming-satelite-vosk.service
- [ ] Интеграция в `_stop()` выполнена - [ ] Интеграция в `_stop()` выполнена
- [ ] `python3 -m py_compile wyoming_satellite/satellite.py` проходит - [ ] `python3 -m py_compile wyoming_satellite/satellite.py` проходит
### ⏳ Этап WS-2A: Протокол команд (ожидается)
- [ ] `handle_websocket_command()` реализован
- [ ] Команда `set_volume` работает
- [ ] Команда `mute`/`unmute` работает
- [ ] Команда `play_sound` работает
- [ ] Команда `set_led_*` работает
- [ ] Команда `trigger_event` работает
- [ ] Обработка ошибок реализована
### ⏳ Этап WS-3: __main__.py (ожидается) ### ⏳ Этап WS-3: __main__.py (ожидается)
- [ ] `--websocket-enabled` аргумент добавлен - [ ] `--websocket-enabled` аргумент добавлен