Newer
Older
smart-home-server / tools / virtual_devices / README.md

Virtual Device Emulator

Эмуляторы устройств умного дома на Python + Flask. Полностью повторяют REST-контракт реальных ESP8266/ESP32 устройств и позволяют разрабатывать и тестировать серверную логику без железа.

Зачем нужен

  • Разрабатывать automation-скрипты (ControlScripts) без реальных устройств
  • Тестировать серверные API: /api/v1/devices/setup/new-device, /devices/action, /events/new
  • Проверять end-to-end цепочку: виртуальная кнопка → событие на сервер → скрипт → виртуальное реле
  • Отлаживать Vue-клиент: устройства отображаются в UI как настоящие

Установка

cd tools/virtual_devices
python3 -m venv .venv
source .venv/bin/activate  # .venv\Scripts\activate на Windows
pip install -r requirements.txt

Быстрый старт

# 1. Создать реле и кнопки
python cli.py create --type relay --alias virt_relay --name "Виртуальное реле" --port 9001 --server-url http://smart-home-serv.local
python cli.py create --type button --alias virt_btn --name "Виртуальные кнопки" --port 9002 --server-url http://smart-home-serv.local

# 2. Запустить
python cli.py start --alias virt_relay
python cli.py start --alias virt_btn

# 3. Зарегистрировать в сервере (они должны быть в режиме setup)
python cli.py register --alias virt_relay --server-url http://smart-home-serv.local
python cli.py register --alias virt_btn --server-url http://smart-home-serv.local

# 4. Посмотреть список
python cli.py list

# 5. Открыть Web UI
# http://127.0.0.1:9001/  — реле
# http://127.0.0.1:9002/  — кнопки

CLI

create

Создаёт файл состояния устройства в devices/<alias>.json.

python cli.py create \
  --type relay|button \
  --alias virt_relay \
  --name "Виртуальное реле" \
  --channels 4 \
  --port 9001 \
  --server-url http://smart-home-serv.local

start

Запускает Flask-эмулятор как background-процесс.

python cli.py start --alias virt_relay [--host 0.0.0.0] [--port 9001]

stop

Убивает процесс по PID-файлу.

python cli.py stop --alias virt_relay

list

Показывает все виртуальные устройства и статус (running / stopped).

python cli.py list

status

Выводит JSON-состояние устройства (каналы, токен, режим и т.д.).

python cli.py status --alias virt_relay

click

Симулирует нажатие кнопки (для button-типа). Шлёт POST /simulate-event на эмулятор.

python cli.py click --alias virt_btn --channel 0

register

Регистрирует устройство в PHP-сервере через стандартный API POST /api/v1/devices/setup/new-device.

python cli.py register --alias virt_relay --server-url http://smart-home-serv.local

Устройство должно быть в режиме setup. Команда:

  1. Проверяет /about
  2. Отправляет POST /api/v1/devices/setup/new-device на сервер
  3. Сервер сам устанавливает токен (/set_token) и переводит устройство в normal

remove

Удаляет файл состояния. Устройство должно быть остановлено.

python cli.py remove --alias virt_relay

Web UI

Каждый эмулятор отдаёт UI на корневом URL (GET /).

Relay

  • Тумблеры Turn ON / Turn OFF для каждого канала
  • Зелёная подсветка — канал on
  • Автообновление статуса каждые 2 секунды

Button

  • Кнопки Click для каждого канала
  • Последнее событие и время
  • Автообновление статуса каждые 2 секунды

Ручное управление через curl

# /about — всегда без авторизации
curl http://127.0.0.1:9001/about | python3 -m json.tool

# Установить токен (в режиме setup)
curl -X POST http://127.0.0.1:9001/set_token \
  -H "Content-Type: application/json" \
  -d '{"token":"debug123"}'

# Получить статус (normal — требует Bearer)
curl http://127.0.0.1:9001/status \
  -H "Authorization: Bearer debug123"

# Переключить реле
curl -X POST http://127.0.0.1:9001/action \
  -H "Authorization: Bearer debug123" \
  -H "Content-Type: application/json" \
  -d '{"action":"set_state","params":{"channel":0,"state":"on"}}'

# Нажать кнопку
curl -X POST http://127.0.0.1:9002/simulate-event \
  -H "Content-Type: application/json" \
  -d '{"event_name":"click","channel":0}'

# Сбросить в setup
curl -X POST http://127.0.0.1:9001/reset \
  -H "Authorization: Bearer debug123"

Полный список эндпоинтов эмулятора

Method Path Auth Описание
GET / Web UI
GET /about Информация об устройстве
GET /status Bearer Состояние каналов
POST /action Bearer Управление (set_state, simulate_click)
POST /set_token — (setup) / Bearer (normal) Установка токена
POST /reset Bearer Сброс в setup, очистка токена
POST /reboot Bearer Stub — заглушка
POST /set_device_name Bearer Смена имени
GET /channels_schema — (setup) / Bearer (normal) Схема 8 каналов
POST /set_channels_schema — (setup) / Bearer (normal) Задать схему
POST /simulate-event Debug: ручной триггер события

Структура

virtual_devices/
├── cli.py              # Click CLI — управление устройствами
├── emulator.py         # Flask-приложение одного эмулятора
├── state.py            # JSON-хранилище DeviceState
├── device/
│   ├── __init__.py
│   ├── base.py         # Базовый класс: auth, channels_schema, reset
│   ├── relay.py        # 4 канала, set_state(on|off)
│   └── button.py       # 4 канала, trigger_click → POST /events/new
├── requirements.txt    # Flask, requests, click
├── devices/            # *.json — состояния (в .gitignore)
├── pids/               # *.pid — PID файлы (в .gitignore)
└── .gitignore          # .venv/, __pycache__/, *.pid

End-to-end пример: нажатие кнопки → сервер → реле

# 1. Создать устройства
python cli.py create --type relay --alias relay_lamp --port 9001 --server-url http://smart-home-serv.local
python cli.py create --type button --alias btn_living --port 9002 --server-url http://smart-home-serv.local

# 2. Запустить
python cli.py start --alias relay_lamp
python cli.py start --alias btn_living

# 3. Зарегистрировать в сервере
python cli.py register --alias relay_lamp --server-url http://smart-home-serv.local
python cli.py register --alias btn_living --server-url http://smart-home-serv.local

# 4. Нажать кнопку
python cli.py click --alias btn_living --channel 0

# 5. Проверить, что реле переключилось (если скрипт настроен)
curl -s http://127.0.0.1:9001/status -H "Authorization: Bearer debug123" | python3 -m json.tool

Ограничения

  • Нет реальной Wi-Fi конфигурации (/setup — заглушка)
  • /reboot — заглушка, процесс не перезапускается
  • События от button — best-effort (requests.post с timeout=3, игнорируются ошибки)
  • Состояние хранится в JSON-файле, без шифрования (только для локальной разработки)
  • Нет OTA-обновлений прошивки
  • Не эмулирует реальные задержки сети и потери пакетов