↩️ На главную

Оптимизация автоматизации: как избавиться от дублирующих MQTT-команд

25.01.2026 | Статья из категории: IOT умный дом

Оптимизация автоматизации зигби эфира и избавление от дублирующих MQTT-команд

В системах умного дома на базе Zigbee2MQTT + кастомной логики часто возникает одна неприятная проблема: правила срабатывают каждую секунду, пока условие истинно, и отправляют команды в MQTT даже тогда, когда устройство уже находится в нужном состоянии.

Это приводит к:

  • Избыточному трафику в сети MQTT,
  • Ненужной нагрузке на Zigbee-устройства,
  • Ускоренному износу реле (особенно механических),
  • Засорению логов.

Сегодня разберём, как это исправить в самописном движке автоматизации на PHP.

Проблема

Допустим, у нас есть правило:

«Если температура == 22°C → включить нагреватель».

Пока температура держится на 22°C, скрипт каждый цикл (раз в секунду) проверяет условие, видит, что оно истинно, и отправляет команду {"state_l3": "ON"} — даже если реле уже включено.

Решение: проверка текущего состояния перед отправкой

Перед отправкой команды будем читать актуальное состояние устройства из базы данных и сравнивать его с желаемым. Если всё совпадает — пропускаем отправку.

Вот ключевая функция на PHP:

// Проверяет, нужно ли пропустить отправку
function shouldSkipAction($rule, $pdo) {
    $actionPayload = json_decode($rule['action_payload'], true);
    if (!is_array($actionPayload)) return false;

    // Определяем топик состояния (без /set)
    $stateTopic = preg_replace('/\/set$/', '', $rule['action_topic']);
    if ($stateTopic === $rule['action_topic']) return false;

    // Получаем последнее значение из sensor_data
    $stmt = $pdo->prepare("SELECT value FROM sensor_data WHERE topic = ? ORDER BY timestamp DESC LIMIT 1");
    $stmt->execute([$stateTopic]);
    $rawValue = $stmt->fetchColumn();
    if ($rawValue === false) return false;

    $currentState = json_decode($rawValue, true);
    if (!is_array($currentState)) return false;

    // Сравниваем каждый ключ из payload
    foreach ($actionPayload as $key => $desiredValue) {
        if (!isset($currentState[$key]) || (string)$currentState[$key] !== (string)$desiredValue) {
            return false;
        }
    }
    return true; // всё совпадает — можно пропустить
}

А в основном обработчике действия:

if (shouldSkipAction($rule, $pdo)) {
    echo "⏭️ Пропускаем действие (устройство уже в нужном состоянии): " . $rule['name'] . "\n";
} else {
    sendMqttCommand($rule['action_topic'], $rule['action_payload'], $rule['name']);
    // ... остальная логика
}

Важный нюанс: структура таблицы sensor_data

В моей БД таблица sensor_data не имеет поля id, только topic, value и timestamp. Поэтому все запросы вида:

ORDER BY id DESC

нужно заменить на:

ORDER BY timestamp DESC

Иначе получите ошибку: Unknown column 'id'.

Результат

Теперь лог выглядит так:

🎉 Сработало правило: свет_fishhouse_OFF_motion
📝 Записано время срабатывания для правила ID 112
⏭️ Пропускаем действие (устройство уже в нужном состоянии): свет_fishhouse_OFF_motion

Правило по-прежнему проверяется каждую секунду (это нормально для триггерной логики), но команда в MQTT отправляется только при реальном изменении состояния.

Совет: Для датчиков движения можно дополнительно задать «Условие должно соблюдаться N секунд», чтобы избежать реакции на кратковременные помехи.

Итог

Минимальные изменения в коде — максимальный эффект:

  • Сеть стала тише,
  • Реле живут дольше,
  • Логи чище,
  • Система осталась полностью совместимой со старыми правилами.

Автоматизация должна быть умной — а не просто шумной 😉

обновленный код mqtt_listener.php




Категории:

Категории

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

← Назад к списку статей

Посетителей сегодня: 0
о блоге | карта блога

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA