Пристрій сам стежить за батареєю, мережею, погодою і може писати в Telegram або змінювати режим інвертора.
Правила — короткий скрипт (схожий на спрощений JavaScript). Файл на пристрої: /automation.js, до 6 КБ.
Перевірка йде кожні ~1.5 с, починаючи з 20-ї секунди після старту.
edge/at теж не змінюється).fn setup() {
}
fn tick() {
if (edge(pid(100) < 50)) {
tg("Мережа пропала, батарея [pid110]%");
}
}
pid(100) — напруга мережі. [pid110] у тексті заміниться на заряд батареї. Так можна підставляти будь-який PID.
Це один і той самий файл. Міняєте в одній вкладці — бачите в іншій після збереження / перемикання.
Картка = одне просте правило: «коли … тоді зробити …». У списку видно назву і вмикач. Клік по назві відкриває одну картку.
У картці можна:
// @vis Мережа пропала
if (edge(pid(100) < 50)) { tg("Мережа пропала"); }
// @endvis
Нова картка з Visual сама ставить ці рядки. Складний код (else, вкладені if, свої формули) пишіть у Script без @vis — тоді картки його не чіпають і не ламають.
Під картками — «Показники на головній» (expose). Шаблони внизу — готові типові сценарії.
Тут живуть змінні, свої функції, if / else, нічний тариф із двома гілками. Visual цей код не затирає.
// Коментар до кінця рядка. Пристрій його ігнорує.
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]%");
}
}
fn setup() — старт: показати значення на головній, початкові set.fn tick() — робоче серце. Усі «якщо батарея…» пишіть сюди.let — тільки зверху, не всередині функції. Ім’я до 11 латинських символів: min_soc ок, night_min_soc — занадто довге.;.if і else завжди фігурні дужки { } (у else if дужки має сам if). Без них Check скаже помилку.//, пристрій викидає з робочої копії (маркери @vis теж). Коментар в кінці робочого рядка лишається і ігнорується парсером.Циклів for / while немає навмисно: скрипт завжди закінчується і не може «зависнути».
Найчастіша помилка новачка: написати 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"); }
Порівняння як у школі: > < >= <= == !=. Логіка: && (і), || (або), ! (не). Дужки ( ) — щоб не плутати порядок.
Можна мішати 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. Гілки перевіряються зверху вниз, спрацює перша правдива.
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 (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 навпаки: якщо прогноз ще «не той», спрацювання на сьогодні вже витрачене.
умова ? якщо_так : якщо_ні
let flag = 0; // зверху файлу, не всередині fn
fn tick() {
flag = pid(509) > 40 ? 1 : 0;
if (edge(flag == 1)) { tg("Спека [pid509]°C"); }
}
У фігурних дужках можна кілька рядків — виконаються підряд.
| Виклик | Що робить |
|---|---|
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).
Годинник пристрою (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…6 | 0 = неділя, 6 = субота |
if ((dow() == 0 || dow() == 6) && at("10:00")) {
tg("Вихідний, SOC [pid110]%");
}
Не розбирайте pid611 у скрипті — для умов є now / at / between. Після ребута почекайте синхронізацію часу (або pid(608) > 2 — аптайм у хвилинах).
До 16 штук. Живуть між циклами tick, скидаються при ребуті й при збереженні скрипта. На флеш самі не пишуться.
let peak = 0;
fn tick() {
peak = max(peak, pid(106));
if (at("23:50")) { peak = 0; }
}
Номер 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 — решта правил цього циклу не виконається.
| Функція | Навіщо |
|---|---|
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 вважаються рівними).
Мережа пропала / з’явилась
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 — «чи ясно зараз», для завтра не підходить.
let, 8 × fn (разом із setup/tick), 8 × expose, 8 × avg, 24 «слоти» для edge/every/held/at._, до 11 символів.tg/log/http/at/…tg в одному циклі — піде лише перший.edge/at — окреме місце в тексті. Не копіюйте «той самий» виклик двічі, думаючи що це один лічильник.if, беріть edge або every.held / avg.pid(n) може бути -1 (ще не прочитано).}, undef, need (, no loops).@vis/@endvis навколо нього, залиште лише в Script.Повний список параметрів — кнопка Список PID на сторінці автоматизації. GPIO 7xx, Tasmota 1000+, BMS 9xx — як у вашій таблиці.
Якщо є /automation_PARAMS.json, а /automation.js ще немає — пристрій сам конвертує правила (без інтернету). JSON лишається копією. Старі правила стають картками з @vis.
Прошивка 4.24+. Check → Зберегти. Складне — у Script без карток; просте — у Visual з @vis.