Skip to content

Commit 1200956

Browse files
Доработка логики и дополнительные проверки
1 parent a0eb1b2 commit 1200956

12 files changed

Lines changed: 924 additions & 93 deletions

CHANGELOG.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,53 @@
4545
не останавливается ровно на пороге и не дребезжит вокруг него.
4646
- `source_url` — блюпринт можно импортировать по ссылке и обновлять из HACS.
4747

48+
### Исправлено во втором раунде ревизии
49+
50+
- **Перепутанные местами минимум и максимум тока поднимали потолок.** При
51+
«минимум 80, максимум 6» блюпринт выдавал 80 А. Теперь при перепутанных
52+
полях побеждает максимум: опустить пол безопасно, поднять потолок — нет.
53+
- **Потолок, не кратный шагу, был недостижим.** При максимуме 28 А и шаге 5 А
54+
мы просили 28, станция округляла по-своему, и уставка переписывалась
55+
на каждом пересчёте. Потолок теперь опускается до кратного шагу.
56+
- **Правильный счётчик сессии отвергался при маленькой цели.** Проверка
57+
«не больше трёх целей» рвалась на цели в 2 кВт·ч, и зарядка не
58+
останавливалась по достигнутой цели. Порог привязан к ёмкости батареи.
59+
- **Режим «доработать начатое» жёг максимум весь день.** В сочетании
60+
с аварийной дозарядкой и выключенным «гасить в конце окна» ток держался
61+
на 28 А до самой цели. Горизонты для этих двух режимов разведены.
62+
- **Watchdog молча отключался без сенсора мощности**, хотя описание обещало
63+
откат на статус станции.
64+
- **Станция, не принимающая команду включения**, получала её на каждом
65+
пересчёте, а хук старта слал уведомление каждые полчаса.
66+
- **Пустое значение режима зарядки** уходило в станцию как пустая опция:
67+
`continue_on_error` глушил ошибку, и режим не выставлялся никогда.
68+
- **Нулевая зона нечувствительности** означала «писать всегда», а не «писать
69+
при любом отличии».
70+
- **Триггер приезда машины** не знал про откат на идентификатор зоны, хотя
71+
сама проверка местоположения его уже использовала.
72+
- Мелочи: в журнале больше не пишется «н/д (none)»; описания троттлинга,
73+
watchdog и режима «доработать начатое» приведены в соответствие с кодом.
74+
75+
### Изменено
76+
77+
- **В зарядку, начатую вручную, автоматизация больше не вмешивается.** Раньше
78+
она подбирала для неё ток, а гасить в конце окна отказывалась. Теперь при
79+
заполненном «флаге сессии» чужая сессия не трогается совсем: ни уставка,
80+
ни выключение. Исключение — неисправность станции. Кабель, воткнутый заранее
81+
при выключенной зарядке, чужой сессией не считается и запускается по обычному
82+
расписанию. Без флага сессии поведение прежнее.
83+
84+
### Добавлено во втором раунде
85+
86+
- Параметр **«Максимальный возраст показания температуры»** (по умолчанию
87+
выключен) — защита от залипшего сенсора для режима холодной погоды.
88+
4889
### Тесты
4990

50-
- 412 тестов вместо 243, 23 мутации вместо 16.
91+
- 474 теста вместо 243, 32 мутации вместо 16.
92+
- `tests/test_actions.py` — блок `actions:` теперь исполняется тестами:
93+
состав и порядок команд, троттлинг, хуки, сброс уставки, шаблон журнала.
94+
Раньше эта часть не проверялась вообще.
5195
- Тестовый движок приведён в соответствие с Home Assistant: `is_number`
5296
отвергает `inf` / `nan`, `states['']` бросает ошибку, `round(0)` возвращает
5397
целое. Раньше движок был мягче реального HA и мог скрыть регрессию.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
## Обязательные требования к правкам
1010

11-
1. **Тесты должны проходить.** `pytest`412 тестов, полный прогон около 3.5 минут.
11+
1. **Тесты должны проходить.** `pytest`474 теста, полный прогон около 7 минут.
1212
2. **Новая переменная — новый тест.** Как минимум нормальный случай и границы.
1313
3. **Порядок объявления переменных имеет значение.** Home Assistant рендерит
1414
`variables:` сверху вниз; ссылка на переменную, объявленную ниже, молча даст

README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
- **Работа без связи с машиной**: при недоступных данных о заряде автоматизация
5454
не падает, а продолжает заряжать по запасному плану.
5555
- **Троттлинг команд** — станции, плохо переносящие частые команды, получают
56-
не больше одной команды в заданный интервал.
56+
не больше одной команды каждого вида в заданный интервал.
5757
- **Аварийная дозарядка** ниже критического процента вне окна — максимальным
5858
током и с запасом над порогом, чтобы не остановиться ровно на нём.
5959
- **Режим холодной погоды**: ниже заданной температуры растяжка отключается.
@@ -66,12 +66,15 @@
6666

6767
```
6868
нужно_кВтч = (цель % − текущий %) / 100 × ёмкость / КПД
69-
осталось_ч = до конца окна − резерв (переход через полночь учтён;
70-
вне окна растяжки нет)
69+
осталось_ч = до конца окна − резерв (переход через полночь учтён)
7170
ток = нужно_кВтч × 1000 / осталось_ч / (U × число фаз)
7271
ток = округлить вверх до шага, зажать в [минимум, максимум]
7372
```
7473

74+
Вне окна зарядка идёт по двум разным поводам, и горизонт для них разный:
75+
аварийная дозарядка спешит и берёт максимум, а «доработать начатое» после
76+
конца окна доливает мягко, чтобы не создавать пик в дневном тарифе.
77+
7578
Ключевое — это замкнутая обратная связь. Если машина берёт меньше, чем задано,
7679
на следующем тике процент окажется ниже плана: `нужно_кВтч` останется прежним,
7780
а `осталось_ч` уменьшится, и ток вырастет. Тот же механизм компенсирует неточность
@@ -209,6 +212,20 @@ https://github.com/saippuakauppias/ha-ev-smart-charging/blob/main/blueprints/aut
209212
Аналогично с геолокацией: потеря позиции запрещает старт новой сессии, но не рвёт
210213
уже идущую. Это отличает «трекер замолчал» от «машина уехала».
211214

215+
### Ручная зарядка
216+
217+
Если заполнить поле «Флаг сессии», автоматизация начнёт отличать свою зарядку
218+
от начатой вручную и **в чужую не вмешивается вовсе**: не подбирает ток и не
219+
гасит её в конце окна. Включили сами — значит и ток выставили какой хотели.
220+
Исключение одно: при неисправности станции зарядка прекращается в любом случае.
221+
222+
Воткнутый заранее кабель при выключенной зарядке чужой сессией не считается —
223+
это обычный способ поставить машину ждать ночи, и в окне она начнёт заряжаться
224+
своим порядком.
225+
226+
Без этого флага отличить одно от другого нечем: Home Assistant не запоминает,
227+
кто щёлкнул выключателем.
228+
212229
Тот же принцип применён к статусу станции. Если сенсор статуса стал нечитаемым
213230
(`unknown` / `unavailable`), новая сессия **не начинается** — отличить
214231
подключённый кабель от пустой розетки нечем, а включение вслепую вдобавок
@@ -220,6 +237,7 @@ https://github.com/saippuakauppias/ha-ev-smart-charging/blob/main/blueprints/aut
220237

221238
| Ситуация | Что было бы | Что делает блюпринт |
222239
|---|---|---|
240+
| Минимальный и максимальный ток перепутаны местами | Ток выше расчётного для проводки | Побеждает максимум: поднять потолок нельзя, опустить пол можно |
223241
| В поле энергии сессии указан накопительный счётчик | «Уже залито 4500 кВт·ч» → цель считается достигнутой | Считает сенсор недоступным и заряжает по резервному плану |
224242
| Сняты все галочки дней недели | Окно не откроется никогда | Трактует как «все дни» |
225243
| Сенсор напряжения врёт (мВ вместо В) | Ток падает до минимума на всю ночь | Отбрасывает значения вне 175–280 В |
@@ -296,8 +314,8 @@ https://github.com/saippuakauppias/ha-ev-smart-charging/blob/main/blueprints/aut
296314

297315
```bash
298316
pip install -r requirements-dev.txt
299-
pytest -m "not slow" # 388 тестов, около 5 секунд
300-
pytest -m slow # мутационное тестирование, около 3.5 минут
317+
pytest -m "not slow" # 441 тест, около 10 секунд
318+
pytest -m slow # мутационное тестирование, около 7 минут
301319
```
302320

303321
| Файл | Что проверяет |
@@ -312,6 +330,7 @@ pytest -m slow # мутационное тестирование, ок
312330
| `test_throttle_and_alarms.py` | Троттлинг команд, зона нечувствительности, контроль исправности |
313331
| `test_triggers.py` | Шаблонные триггеры, в том числе на пустом наборе сущностей |
314332
| `test_night_simulation.py` | Сквозная симуляция ночи в замкнутом контуре, включая приезд и отъезд машины, неисправность, потерю связи, мороз и watchdog |
333+
| `test_actions.py` | Команды, уходящие станции: состав, порядок, троттлинг, хуки |
315334
| `test_mutations.py` | Проверка самих тестов: ловят ли они намеренно сломанный блюпринт |
316335

317336
Последний файл самый ценный. Он моделирует батарею и прогоняет регулятор
@@ -325,7 +344,7 @@ pytest -m slow # мутационное тестирование, ок
325344

326345
Зелёный набор тестов сам по себе ничего не доказывает — тесты бывают
327346
бессодержательными. `test_mutations.py` намеренно ломает блюпринт по одному месту
328-
за раз (23 мутации, каждая соответствует обещанию из этого README) и убеждается,
347+
за раз (32 мутации, каждая соответствует обещанию из этого README) и убеждается,
329348
что остальной набор это замечает. Выжившая мутация означает, что соответствующее
330349
поведение не покрыто.
331350

0 commit comments

Comments
 (0)