↩ Назад

English

Автоматизація інвертора

Пристрій сам стежить за батареєю, мережею, погодою і може писати в Telegram або змінювати режим інвертора. Правила — короткий скрипт (схожий на спрощений JavaScript). Файл на пристрої: /automation.js, до 6 КБ.

Перевірка йде кожні ~1.5 с, починаючи з 20-ї секунди після старту.

1. З чого почати

  1. Відкрийте сторінку Автоматизація.
  2. Вкладка Script — повний текст. Visual — прості картки.
  3. Натисніть Check — пристрій перевірить синтаксис без Telegram, HTTP і записів в інвертор (стан edge/at теж не змінюється).
  4. Якщо OK — Зберегти.
Найменший робочий скрипт. Скопіюйте, Check, Зберегти. Якщо мережа пропаде — прийде повідомлення.
fn setup() {
}

fn tick() {
  if (edge(pid(100) < 50)) {
    tg("Мережа пропала, батарея [pid110]%");
  }
}

pid(100) — напруга мережі. [pid110] у тексті заміниться на заряд батареї. Так можна підставляти будь-який PID.

2. Дві вкладки: Visual і Script

Це один і той самий файл. Міняєте в одній вкладці — бачите в іншій після збереження / перемикання.

Visual — прості картки

Картка = одне просте правило: «коли … тоді зробити …». У списку видно назву і вмикач. Клік по назві відкриває одну картку.

У картці можна:

Visual показує лише блоки, обгорнуті так:
  // @vis Мережа пропала
  if (edge(pid(100) < 50)) { tg("Мережа пропала"); }
  // @endvis
Нова картка з Visual сама ставить ці рядки. Складний код (else, вкладені if, свої формули) пишіть у Script без @vis — тоді картки його не чіпають і не ламають.

Під картками — «Показники на головній» (expose). Шаблони внизу — готові типові сценарії.

Script — повна мова

Тут живуть змінні, свої функції, if / else, нічний тариф із двома гілками. Visual цей код не затирає.

3. Скелет скрипта

// Коментар до кінця рядка. Пристрій його ігнорує.

let min_soc = 25;          // глобальна змінна (пам'ять між циклами)

fn setup() {               // один раз після збереження або ребута
  expose(800, "Поріг", "%");
  set(800, min_soc);
}

fn tick() {                // кожні ~1.5 секунди
  set(800, min_soc);

  if (pid(110) < min_soc && every(600)) {
    tg("Батарея [pid110]%");
  }
}

Циклів for / while немає навмисно: скрипт завжди закінчується і не може «зависнути».

4. Коли спрацьовує правило

Найчастіша помилка новачка: написати if (pid(110) < 30) { tg("..."); } — і Telegram сипле кожні 1.5 с. Оберіть тип спрацювання:

ХочуПишітьПростими словами
Один раз, коли сталося if (edge(УМОВА)) { ... } Спрацює в момент переходу «ні → так». Після ребута вже-так не стріляє знову.
Нагадувати, поки умова тримається if (УМОВА && every(600)) { ... } Раз на 600 с (10 хв), поки умова true. every ставте справа. Перший виклик лише заводить таймер (ще false) — перше спрацювання через N секунд.
Чекати, поки протримається N секунд if (held(УМОВА, 300)) { ... } Стає true і лишається true кожен цикл, поки умова жива. Для одноразової дії: edge(held(…, 300)). Скидання — щойно умова стала false.
Рівно о 23:00, раз на добу if (at("23:00")) { ... } Один раз на календарну добу в цю хвилину. Після ребута в ту саму хвилину може спрацювати ще раз.
Весь час, поки true if (УМОВА) { ... } Кожен цикл. Для Telegram / запису в інвертор майже завжди зайве.
Мінімум — мережа щойно пропала:
if (edge(pid(100) < 50)) { tg("Мережа пропала"); }
Максимум — кілька «коли» в одному tick:
if (edge(pid(100) < 50)) { tg("Мережа пропала, SOC [pid110]%"); }
if (edge(pid(100) > 50)) { tg("Мережа [pid100]V"); }
if (pid(110) < 25 && every(600)) { tg("Низький SOC [pid110]%"); }
if (pid(509) > 40 && every(1800)) { tg("Спека [pid509]°C"); }

5. Умови: if, else, кілька рівнів

Порівняння як у школі: > < >= <= == !=. Логіка: && (і), || (або), ! (не). Дужки ( ) — щоб не плутати порядок.

Можна мішати PID між собою: pid(106) - pid(117) > 1500 (надлишок сонця).

Голий if без edge/every виконується кожні ~1.5 с. Telegram у такій гілці засипле чат (і все одно ліміт — 1 повідомлення за цикл). Нижче — лише синтаксис гілок; у реальних правилах додавайте edge або every.

Один рівень

if (pid(110) < 20) {
  set(800, 1);
} else {
  set(800, 0);
}

set(800, …) на expose-змінну головної безпечний кожен цикл (це RAM, не команда інвертору). Для pid132 так робити не варто.

Кілька рівнів (else if)

Мова розуміє else if. Гілки перевіряються зверху вниз, спрацює перша правдива.

if (pid(110) < 15) {
  set(800, 1);
} else if (pid(110) < 30) {
  set(800, 2);
} else if (pid(110) > 90) {
  set(800, 3);
} else {
  set(800, 0);
}

Вкладений if всередині if

if (at("23:00")) {
  if (pid(513) >= 3000 && pid(110) >= 25) {
    set(132, 2);
  } else {
    set(132, 1);
    set(131, 2);
    tg("Нічний заряд, завтра [pid513]");
  }
}
Для at зручніше спочатку «чи зараз 23:00», а вже всередині — погода / SOC. Не пишіть at("23:00") && pid(513) < 1000 навпаки: якщо прогноз ще «не той», спрацювання на сьогодні вже витрачене.

Короткий запис без if

умова ? якщо_так : якщо_ні

let flag = 0;          // зверху файлу, не всередині fn
fn tick() {
  flag = pid(509) > 40 ? 1 : 0;
  if (edge(flag == 1)) { tg("Спека [pid509]°C"); }
}

6. Дії

У фігурних дужках можна кілька рядків — виконаються підряд.

ВикликЩо робить
set(132, 2)Записати параметр (режим інвертора, GPIO, Tasmota, web-кнопка). Для списків — номер пункту, не ампери: дивіться «Список PID».
tg("текст [pid110]%")Повідомлення в Telegram. Одне на цикл tick. Якщо підписка закінчилась — не відправляється.
log("...")Рядок у журналі пристрою (код 12). До 3 за цикл, текст до 64 символів.
http("http://192.168.0.4/cm?cmnd=Power+1")GET у LAN, таймаут ~0.6 с, без очікування відповіді. Один за цикл. Пробіл у URL → +. Запит на IP самого пристрою ігнорується.
Мінімум — одна дія:
if (at("07:00")) { set(132, 2); }
Максимум — кілька дій разом:
if (pid(513) < 1000 && at("23:00")) {
  set(132, 1);
  set(138, 4);
  tg("Нічний заряд, завтра [pid513] Wh/m², SOC [pid110]%");
  log("night charge");
}
set на PID інвертора (пріоритети, струми) — реальна зміна налаштувань. Спочатку Check, потім збережіть. Для проби зручний pid звуку (часто 123) або змінна на головній (800–807).

7. Час доби

Годинник пристрою (NTP). now() — число ГГХХ: 7:30 → 730, 23:00 → 2300.

ФункціяПрикладКоли true
between("07:00", "21:00")деньЗ 07:00 по 21:00 включно
between("22:00", "06:00")нічЧерез північ: 22:00…00:00…06:00
at("23:00")моментОдин раз на добу в цю хвилину (після ребута в ту саму хвилину — знову)
hm("07:30")→ 730Щоб писати now() >= hm("07:00") && now() < hm("21:00")
dow()0…60 = неділя, 6 = субота
if ((dow() == 0 || dow() == 6) && at("10:00")) {
  tg("Вихідний, SOC [pid110]%");
}

Не розбирайте pid611 у скрипті — для умов є now / at / between. Після ребута почекайте синхронізацію часу (або pid(608) > 2 — аптайм у хвилинах).

8. Змінні, expose, свої функції

Змінні

До 16 штук. Живуть між циклами tick, скидаються при ребуті й при збереженні скрипта. На флеш самі не пишуться.

let peak = 0;

fn tick() {
  peak = max(peak, pid(106));
  if (at("23:50")) { peak = 0; }
}

Показник на головній — expose

Номер PID 800–807 (або вільний). Не потрапляє в графіки дня.

fn setup() {
  expose(800, "Пік PV", "W");
}
fn tick() {
  set(800, peak);
}

У Visual те саме — блок під картками.

Свої функції

Разом до 8 функцій, рахуючи setup і tick (тобто до 6 своїх). Без параметрів, ім’я до 11 символів. return — результат.

fn excess() {
  return pid(106) - pid(117);
}

fn tick() {
  if (edge(held(excess() > 1500 && pid(110) > 80, 300))) {
    set(1000, 1);
  }
}

return; всередині tick — решта правил цього циклу не виконається.

9. Довідник функцій

ФункціяНавіщо
pid(n)Поточне значення. Порожньо → -1.
set(n, v)Запис параметра.
tg("...") log("...") http("...")Telegram / журнал / HTTP GET.
now() hm("HH:MM")Час як ГГХХ.
between("A","B")Проміжок; якщо A > B — через північ.
at("HH:MM")Один раз на календарну добу в цю хвилину. Після ребута в ту саму хвилину може спрацювати ще раз.
dow()День тижня 0…6.
edge(x) every(sec) held(x, sec)Фронт / період / утримання. Стан прив’язаний до місця виклику в тексті.
avg(pid, sec)Плавне середнє PID (менше хибних спрацювань).
min(a,b) max(a,b) abs(x)Математика.
expose(pid,"ім’я","од.")Показник на головній. До 8.

Також: + - * / %, порівняння, && || !, ?:. == з допуском 0.001 (25 і 25.0005 вважаються рівними).

10. Готові рецепти

Мережа пропала / з’явилась

if (edge(pid(100) < 50)) {
  tg("Мережа пропала! Батарея [pid110]% ([pid109]V)");
}
if (edge(pid(100) > 50)) {
  tg("Мережа [pid100]V");
}

Два окремі if, не else if: інакше другий edge не оновить стан, поки спрацював перший, і «мережа з’явилась» можна пропустити.

Ніч на батареї, якщо SOC високий

if (edge(between("22:00", "06:00") && pid(110) > 60)) {
  set(132, 2);
  tg("Ніч, батарея [pid110]%");
}

Надлишок PV 5 хв → розетка Tasmota (pid1000)

if (edge(held(pid(106) - pid(117) > 1500 && pid(110) > 80, 300))) {
  set(1000, 1);
}
if (edge(pid(110) < 40 || pid(106) < 500)) {
  set(1000, 0);
}

held сам по собі далі true кожен цикл — без edge команда set молотила б реле кожні 1.5 с.

GPIO: світло о 18:00 / 22:00 (режим піна задається в налаштуваннях параметра)

if (at("18:00")) { set(704, 1); }
if (at("22:00")) { set(704, 0); }

Складніше: нічний заряд за прогнозом + ранок SBU
Пишіть у Script без @vis. Пороги й індекси pid131/132/138 залежать від вашого інвертора — дивіться «Список PID».

let sun_ok = 3000;
let min_soc = 25;
let grid_a = 4;

fn tick() {
  if (at("23:00")) {
    if (pid(513) >= sun_ok && pid(110) >= min_soc) {
      set(131, 3);
      set(132, 2);
    } else {
      set(132, 1);
      set(131, 2);
      set(138, grid_a);
      tg("Нічний заряд. Завтра [pid513], SOC [pid110]%");
    }
  }
  if (at("07:00")) {
    set(132, 2);
    set(131, 1);
  }
}

pid513 — прогноз сонця на завтра (Wh/m²). pid506 — «чи ясно зараз», для завтра не підходить.

11. Обмеження і типові помилки

Часті помилки

Повний список параметрів — кнопка Список PID на сторінці автоматизації. GPIO 7xx, Tasmota 1000+, BMS 9xx — як у вашій таблиці.

12. Міграція зі старого JSON

Якщо є /automation_PARAMS.json, а /automation.js ще немає — пристрій сам конвертує правила (без інтернету). JSON лишається копією. Старі правила стають картками з @vis.

Відкат на прошивку ≤ 4.23 читає лише JSON на момент конвертації. Правки вже в скрипті назад не поїдуть. Спочатку ставте beta OTA, переконайтесь, потім стабільну.

Прошивка 4.24+. Check → Зберегти. Складне — у Script без карток; просте — у Visual з @vis.