❮  Интеграции

Спецификация вебхука

Entrixy отправляет на ваш адрес команду открыть или закрыть и подписывает её. Здесь формат запроса и способ проверить подпись, чтобы принимать команду в своём сервисе, умном доме или на плате.

Вебхук — режим, в котором открытие исполняет не наш контроллер, а ваша собственная система: Home Assistant, реле в локальной сети, свой сервер. Объект «Вебхук» в обоих режимах отправляет один и тот же подписанный запрос. С вашего телефона: запрос отправляет телефон владельца, адрес и секрет не покидают телефон. С сервера Entrixy: запрос отправляет наш сервер, поэтому он хранит адрес и секрет. Готовые приёмники: curl, Flask, Home Assistant.

Запрос

При нажатии Entrixy отправляет POST на ваш webhook_url с JSON-телом:

POST <ваш webhook_url>
Content-Type: application/json

{
  "action":    "open",
  "object_id": 42,
  "timestamp": 1750000000,
  "nonce":     "0011223344556677",
  "signature": "9d7b58066094fa88..."
}
ПолеЗначение
action"open", для объекта «Открыть / закрыть» также "close"
object_idid объекта в вашем аккаунте
timestampunix-время отправки (секунды)
nonce16 hex-символов (8 случайных байт), одноразовый
signatureHMAC-SHA256, см. ниже

Объект «Открыть / закрыть» чередует команды по последнему известному положению: close — если он открыт, иначе open. В режиме «С сервера» решает сервер, в режиме «С телефона» — телефон владельца.

Подпись

Подписывается каноническая строка из четырёх полей через точку, а не само JSON-тело — так вам не придётся побайтно повторять нашу сериализацию:

base = "<timestamp>.<nonce>.<action>.<object_id>"
signature = HMAC-SHA256(webhook_secret, base)   // hex

Приложение само создаёт webhook_secret при добавлении объекта «Вебхук»: 32 случайных шестнадцатеричных символа. Скопируйте его кнопкой рядом с полем в настройки своего приёмника. Секрет можно заменить своим. Вебхук без секрета не сохраняется ни в одном из режимов, поэтому каждый запрос подписан.

Гости секрет не получают никогда: их нажатия подписывает телефон владельца или сервер — тот, кто отправляет запрос.

Как проверить (получатель)

  1. Соберите ту же строку base = timestamp.nonce.action.object_id из принятых полей.
  2. Посчитайте HMAC-SHA256(webhook_secret, base) и сверьте с signature через constant-time сравнение (hash_equals / hmac.compare_digest).
  3. Анти-replay: отклоните, если |now − timestamp| > 300 секунд. По желанию — храните недавние nonce и отклоняйте повтор.
  4. Подпись совпала, запрос свежий — выполняйте открытие.
Адрес вебхука обычно открыт без аутентификации, поэтому проверка подписи обязательна: без неё открыть может любой, кто узнал адрес. Если меняете секрет, берите не короче 16 символов, например из openssl rand -hex 32. Никогда не вставляйте секрет в сам адрес.

Код проверки

Каждый пример проверяет подпись и время. Храните секрет в настройках приёмника или в переменной окружения.

PHP

$secret = getenv('ENTRIXY_WEBHOOK_SECRET');
$b = json_decode(file_get_contents('php://input'), true);
$base = $b['timestamp'] . '.' . $b['nonce'] . '.' . $b['action'] . '.' . $b['object_id'];
$ok = hash_equals(hash_hmac('sha256', $base, $secret), (string)($b['signature'] ?? ''))
   && abs(time() - (int)$b['timestamp']) <= 300;
if (!$ok) { http_response_code(401); exit; }
// $b['action'] is "open" or "close"

Python

import hashlib, hmac, os, time

def verify(b: dict) -> bool:
    secret = os.environ["ENTRIXY_WEBHOOK_SECRET"].encode()
    base = f'{b["timestamp"]}.{b["nonce"]}.{b["action"]}.{b["object_id"]}'
    mac = hmac.new(secret, base.encode(), hashlib.sha256).hexdigest()
    return (hmac.compare_digest(mac, str(b.get("signature", "")))
            and abs(time.time() - int(b["timestamp"])) <= 300)

Node.js

const crypto = require('crypto');

function verify(b, secret) {
  const base = `${b.timestamp}.${b.nonce}.${b.action}.${b.object_id}`;
  const mac = crypto.createHmac('sha256', secret).update(base).digest('hex');
  const sig = String(b.signature || '');
  return sig.length === mac.length
    && crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(sig))
    && Math.abs(Date.now() / 1000 - b.timestamp) <= 300;
}

Чтобы отвергать и повтор в пределах пяти минут, запоминайте значения nonce до тех пор, пока их timestamp не станет старше 300 секунд, и отклоняйте повторы.

Тест-вектор

Прогоните свою проверку на этих значениях и сверьте подпись:

webhook_secret = webhook_secret_example_key
timestamp      = 1750000000
nonce          = 0011223344556677
action         = open
object_id      = 42

base       = 1750000000.0011223344556677.open.42
signature  = 9d7b58066094fa88acc0afa7aa805fc5f0d008d33cb11b8aa2366102da8e20f1
  = HMAC-SHA256(webhook_secret, base)

Ответ

Чтобы приложение показало результат, ответьте в формате JSON:

{"level":"success","message":"Opened","position":"open","close_in":30}
ПолеЗначение
levelsuccess, warning, danger или info: как приложение отметит результат
messageтекст, который приложение покажет рядом с нажатием
positionдля объекта «Открыть / закрыть»: open, closed или unknown
close_inчерез сколько секунд приёмник закроет сам, не больше 3600: по их истечении приложение покажет объект закрытым

Прежний ответ с полем status (ok или error) тоже понимается. Если нет ни того, ни другого поля, решает HTTP-код: любой ответ 2xx — успех, всё остальное — неудача. Поэтому достаточно и пустого 200 OK.

Без close_in положение показывается две секунды после ответа, потом становится неизвестным: следующее нажатие открывает.

Отправка

Запрос всегда приходит методом POST с телом в формате JSON, никогда простым GET.

Поэтому приёмник, доступный только в домашней сети, работает, лишь когда запрос отправляет телефон владельца, и только пока телефон подключён к этой сети. Для приёмника, доступного из интернета, используйте https.

Попробовать без своего приёмника

Откройте демо-шлагбаум: он выдаст вам личный проверочный адрес. Укажите его адресом объекта и вставьте секрет объекта в поле на той странице — подпись каждого нажатия проверяется прямо в браузере, а секрет никуда не отправляется.

Готовые приёмники

curl отправляет тестовый запрос с правильной подписью. Flask — приёмник, который проверяет подпись и отклоняет повторы. Home Assistant принимает команду через этот приёмник или через собственный триггер вебхука без проверки подписи.

Реле Shelly и Tasmota, а также openHAB подпись не проверяют. Они подключаются через клиент на Node.js: он сам держит соединение с Entrixy и передаёт команду дальше внутри вашей сети.