В системах умного дома на базе 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 секунд», чтобы избежать реакции на кратковременные помехи.
Итог
Минимальные изменения в коде — максимальный эффект:
- Сеть стала тише,
- Реле живут дольше,
- Логи чище,
- Система осталась полностью совместимой со старыми правилами.
Автоматизация должна быть умной — а не просто шумной 😉
Комментарии
Пока нет комментариев. Будьте первым!