Перейти к содержанию

Отказоустойчивость

Без присмотра ломается ровно то, что на глазах работает: облачная интеграция отдаёт закэшированный процент, трекер «теряет» стоящую машину, станция на секунду уходит в недоступность. Ниже — что блюпринт делает с каждым из этих отказов и почему именно так.

Достоверность данных о заряде

Достоверность данных о заряде проверяется по трём признакам, строго в этом порядке:

Проверка Что означает Что делает блюпринт
Сущность недоступна Чаще всего слетела авторизация интеграции car_data_unavailable_check_integration_auth
Данные устарели Машина давно не выходила на связь car_data_stale
Процент «замёрз» Значение не меняется, хотя ток идёт car_data_frozen

Замёрзший процент — худший из отказов

Третья проверка нужна потому, что многие облачные интеграции при пропаже связи с машиной не уходят в unavailable, а продолжают отдавать последнее закэшированное значение сколь угодно долго. Для регулятора это худший из отказов: замёрзший процент заставил бы держать максимальный ток всю сессию.

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

Если зарядка уже идёт, ток при этом не снижается: резервное значение — это ответ на вопрос «с чего начинать», а не «до чего опускаться». Уменьшать ток, которого и так могло не хватать, значит увеличивать отставание, и процент после этого растёт ещё медленнее. Поэтому берётся большее из двух: резервный ток и тот, что уже стоит на станции.

Процент, дошедший до цели, замёрзшим не считается: перестать меняться ему положено, а станция в это время ещё отдаёт мощность на балансировку ячеек. Иначе достигнутая цель выглядела бы как отказ данных, и зарядка не выключилась бы до конца окна.

Трекер, который врёт

Трекеры регулярно «теряют» стоящую машину, причём обычно не молчанием, а бодрым not_home: координаты уплывают на пару сотен метров, и машина формально выходит из зоны. Ни один из этих сбоев не должен гасить зарядку.

Различить враньё от настоящего отъезда позволяет сама станция: уехать, не отключив кабель, невозможно. Поэтому пока станция отдаёт ток, никакой not_home идущую сессию не прерывает — доверяем железу, а не спутникам. Проверяется именно ток (сенсор мощности), а не статус станции: статус умеет залипать, ток — нет. Если сенсора мощности нет, используется статус.

Настоящий отъезд — трекер вне зоны и тока нет — останавливает зарядку как раньше, с причиной car_not_home.

Обрыв связи со станцией при этом за «тока нет» не считается. Иначе достаточно было бы совпадения двух рядовых сбоев — вравшего трекера и секундной потери связи, — чтобы блюпринт решил, что машина уехала, и погасил исправно идущую зарядку. «Не знаем» и «нет» — разные ответы.

Потеря позиции (unavailable / unknown) тоже не рвёт идущую сессию, а лишь запрещает старт новой.

Почему для старта правило другое

Ток подтверждает присутствие только у идущей сессии. Пока зарядка не включена, тока нет, и подтвердить нечем — поэтому начать зарядку при машине вне зоны нельзя. Это защита от того, чтобы не начать заряжать чужую машину, если кто-то воткнёт свой автомобиль в вашу станцию.

Одной проверкой в момент начала окна дело при этом не ограничивается. Условие перепроверяется на каждом пересчёте всю сессию, поэтому сбой GPS в неудачную минуту стоит не всей сессии, а лишь задержки до ближайшего пересчёта:

Когда трекер пришёл в себя Что происходит
00:20 Старт в 00:20, цель достигнута
02:00 Старт в 02:05, цель достигнута
05:00 Старт в 05:05, дозарядка сколько успеет

Кроме таймера есть и мгновенный триггер: переход трекера в домашнюю зону запускает пересчёт сразу, не дожидаясь очередного тика. То есть при штатном поведении GPS задержки не будет вообще.

А вот если трекер соврал уже после старта — зарядка не прервётся, как описано выше. Правила разные намеренно: начать вслепую рискованно, прервать идущую зарядку — вредно.

В пятую ночь обкатки это сработало вживую: трекер ушёл в not_home за 48 минут до конца окна и не вернулся, машина всё это время стояла в гараже и брала 12.6 А. Зарядку не тронули, до цели дошли.

Защита действует, пока идёт ток

Подтверждает присутствие машины именно ток, поэтому после штатного выключения подтверждать нечем — и с этого момента совравший трекер начинает выглядеть как уехавшая машина. На поведение это не влияет (заряжать в этот момент уже нечего), но в журнале причина бездействия указывалась бы неверно, поэтому «машина не дома» объясняет только два случая: почему гасим идущую зарядку и почему не начинаем новую.

Станция в закрытом гараже

Если станция стоит в гараже и заряжать на ней можете только вы, проверку зоны проще выключить совсем — снимите галку «Заряжать только когда машина в зоне».

Ручная зарядка

Если заполнить поле «Флаг сессии», автоматизация начнёт отличать свою зарядку от начатой вручную и в чужую не вмешивается: не подбирает ток и не гасит её в конце окна. Включили сами — значит и ток выставили какой хотели.

Уважение это распространяется ровно на то, что человек действительно выбирал: на ток и на длительность. Всё остальное к его выбору отношения не имеет и останавливает зарядку независимо от того, кто её начал:

Причина остановки Чужая сессия
Неисправность станции Останавливаем
Машина уехала (и ток не идёт) Останавливаем
Кабель выдернут Останавливаем
Цель достигнута Останавливаем
Станция сообщила «заряжено» Останавливаем
Окно кончилось Не трогаем
Ток разошёлся с планом Не трогаем

Раньше всё это висело на одном признаке, и стоило ему ошибиться — отключались сразу все остановки. Ровно так и вышло во вторую реальную ночь (см. ниже).

Воткнутый заранее кабель при выключенной зарядке чужой сессией не считается — это обычный способ поставить машину ждать окна, и в нём она начнёт заряжаться своим порядком.

Своя остановка не превращается в чужую сессию

Останавливая зарядку, автоматизация гасит выключатель и следом опускает флаг сессии. Флаг падает мгновенно, а станция подтверждает выключение через несколько секунд — в шестую ночь через 43. Всё это время выключатель ещё включён, а флаг уже опущен, то есть выглядит это в точности как ручная зарядка.

Отличает их возраст выключателя: наша команда ушла секунды назад, а человек щёлкает выключателем задолго до пересчёта. Без этой оговорки проглоченная станцией команда выключения означала бы, что зарядка льётся до утра, а автоматизация вежливо не вмешивается в собственную сессию.

Без этого флага отличить одно от другого нечем: Home Assistant не запоминает, кто щёлкнул выключателем.

Тот же принцип применён к статусу станции. Если сенсор статуса стал нечитаемым (unknown / unavailable), новая сессия не начинается — отличить подключённый кабель от пустой розетки нечем, а включение вслепую вдобавок подняло бы ложную тревогу «включено, но тока нет». Уже идущая зарядка при этом не прерывается.

Станция, потерявшая связь

Дешёвые станции (Tuya и родня) регулярно отваливаются на секунду-другую. Выглядит это неприятнее, чем кажется: интеграция роняет все сущности устройства разом, а поднимает их не одновременно — в реальной ночи восстановление заняло четыре такта за две секунды.

Такт Что уже вернулось
+0.0 с ничего: выключатель, статус, уставка, связь — всё недоступно
+0.6 с статус (charging)
+1.5 с выключатель (on)
+1.9 с сенсор связи

Опасен здесь первый такт. Выключатель в нём «не включён» — но не потому, что его выключили, а потому что его нет. Ровно на этом блюпринт однажды и погорел: уборка прочитала недоступность как ручное выключение и погасила флаг сессии, а через полторы секунды выключатель вернулся — и своя же зарядка до утра считалась чужой.

Отсюда общее правило: воздержаться от действия можно по любому сомнению, а вот разрушительный вывод — «выключили», «чужая», «сломалась» — делается только по подтверждённому состоянию. Практически это значит:

  • недоступный выключатель не считается выключенным — в том числе когда по нему судят, идёт ли ток: иначе обрыв связи посреди зарядки читался бы как «машина уехала», стоило трекеру в тот же момент ошибиться;
  • сенсор неисправности читается только при живой связи и читаемом статусе — иначе обрыв выглядел бы поломкой и поднимал ложную тревогу;
  • нечитаемый флаг сессии означает «владелец неизвестен», а не «чужая» (при перезапуске Home Assistant input_boolean проходит через unknown);
  • переходы сущностей в недоступность не запускают пересчёт вовсе: считать по ним нечего, а в режиме restart каждый такой прогон обрывает предыдущий.

Если флаг всё же потерялся при живой зарядке, автоматизация забирает владение обратно — но только когда уставка на станции сходится с расчётом. Это и отличает свою сессию: включая зарядку, блюпринт сначала пишет ток, и у его собственной сессии уставка совпадает с планом. Человек ставит ток по своему разумению, и попасть в плавающий расчёт может разве что случайно.

Команда, которую станция проглотила

Те же дешёвые станции иногда просто теряют команду. Не отвечают ошибкой, не отваливаются — принимают и ничего не делают. На третьей ночи обкатки так пропало включение зарядки: команда ушла в 23:06, выключатель остался off.

Коварство в том, что промах команды не порождает события. Выключатель как был выключен, так и остался; уставка как была 11, так и осталась. Обычные триггеры здесь бесполезны — они ждут либо часов, либо изменения состояния. Поэтому промах жил до следующего пересчёта: зарядка началась на 24 минуты позже, а каждая потерянная запись тока стоила получаса регулирования.

Потерянное включение лечится триггером на неизменность: выключатель, пролежавший в off дольше паузы между командами, — это либо «мы ещё не начинали», либо «команда пропала», и прогон разбирается сам. У исправной станции триггер молчит: выключатель уходит в on сразу.

Приём работает потому, что off — состояние покоя: выключатель попадает в него настоящим переходом (в конце прошлой сессии или когда станция отключилась сама), и отсчёт уже идёт, когда команда пропадает.

Потерянную запись тока так поймать нельзя

«Неверный ток» состоянием покоя не является — лежать в нём нечему, и взводить отсчёт нечем. Триггер на состояние в Home Assistant требует смены значения, а проглоченная запись не меняет ничего: события нет.

Поэтому потерянная уставка живёт до ближайшего пересчёта — в худшем случае полный интервал. Обнаруживает её обычная проверка расхождения с планом, и следующая же команда уходит повторно. В пятую ночь обкатки так и вышло: запись 13 А в 03:30 пропала, ток полчаса стоял на 12 А, в 04:00 команда ушла снова и подействовала. Ценой были 0.2 % заряда.

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

Отдельный триггер на принятую уставку нужен для другого — для очереди команд: уставка, простоявшая паузу, означает «станция приняла запись, можно слать следующую», и включение уходит сразу, не дожидаясь общего интервала.

Повторы при этом не превращаются в долбёж: включение по-прежнему требует подтверждённого off и выдержанной паузы, а запись тока — расхождения с планом. Станция, которая просто выключена вне окна, никаких команд не получает.

Два триггера, совпавшие по времени

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

Случай не экзотический. Круглое время начала окна попадает на сетку пересчёта: в шестую ночь обкатки старт был выставлен на 23:30, и «начало окна» с обычным пересчётом дали два прогона с разницей 204 миллисекунды — оба успели дойти до записи тока. В станцию ушли две одинаковые команды подряд, ровно с тем интервалом, от которого пауза и защищает.

Поэтому блюпринт смотрит не только на возраст сущностей, но и на то, когда запускался он сам. Прогон, стартовавший меньше секунды после предыдущего, — это гонка триггеров, а не новая мысль, и команд он не шлёт. Порог заведомо меньше любой осмысленной паузы между командами, так что на обычную работу это не влияет; если паузу выставить в ноль, проверка отключается вместе с ней.

Ничего настраивать не нужно, и совпадающее с сеткой время старта менять тоже не нужно — это штатный случай.

Отдельно блюпринт защищён от нескольких «тихих» ошибок настройки, каждая из которых иначе навсегда и без единого сообщения отключила бы зарядку:

Ситуация Что было бы Что делает блюпринт
Минимальный и максимальный ток перепутаны местами Ток выше расчётного для проводки Побеждает максимум: поднять потолок нельзя, опустить пол можно
В поле энергии сессии указан накопительный счётчик «Уже залито 4500 кВт·ч» → цель считается достигнутой Считает сенсор недоступным и заряжает по резервному плану
Сняты все галочки дней недели Окно не откроется никогда Трактует как «все дни»
Сенсор напряжения врёт (мВ вместо В) Ток падает до минимума на всю сессию Отбрасывает значения вне 175–280 В
У зоны нет отображаемого имени Машина никогда не «дома» Откатывается на идентификатор зоны