smart-home-server / tools / virtual_devices /
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 25 days ago
..
device Phase 2: Add platform, channels, core_version to virtual emulator and OTA endpoint 25 days ago
devices Add virtual device emulator (Flask) for relay and button types 26 days ago
pids Add virtual device emulator (Flask) for relay and button types 26 days ago
.gitignore Add virtual device emulator (Flask) for relay and button types 26 days ago
README.md Add comprehensive documentation for virtual device emulator 25 days ago
cli.py Add virtual device emulator (Flask) for relay and button types 26 days ago
emulator.py Phase 2: Add platform, channels, core_version to virtual emulator and OTA endpoint 25 days ago
requirements.txt Add virtual device emulator (Flask) for relay and button types 26 days ago
state.py Phase 2: Add platform, channels, core_version to virtual emulator and OTA endpoint 25 days ago
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-обновлений прошивки
  • Не эмулирует реальные задержки сети и потери пакетов