[2026-09-17] eagle: family/how-to/rasputin-router.md family/how-to/zont-config-compiler.md family/tech/zont-api.md
This commit is contained in:
@@ -545,20 +545,54 @@ default via 192.168.2.2 dev eth0 proto static src 192.168.2.157
|
|||||||
|
|
||||||
То есть Rasputin получает адрес `192.168.2.157` **от роутера `192.168.2.2`** и выходит в интернет через него, а `192.168.2.2` — через GPON `192.168.0.1`. Топология запроса подтверждена: `rasputin → 192.168.2.2 → 192.168.0.1 (GPON) → 192.168.0.50`.
|
То есть Rasputin получает адрес `192.168.2.157` **от роутера `192.168.2.2`** и выходит в интернет через него, а `192.168.2.2` — через GPON `192.168.0.1`. Топология запроса подтверждена: `rasputin → 192.168.2.2 → 192.168.0.1 (GPON) → 192.168.0.50`.
|
||||||
|
|
||||||
### План ремонта (НЕ применён, ждёт апрува)
|
### ✅ ЧТО СДЕЛАНО (2026-09-17) — правило `ip rule` добавлено и работает
|
||||||
|
|
||||||
|
Применено и **проверено Alex руками** (80 порт на `192.168.0.50` открылся):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# 1. ip rule — расширить bypass на GPON-сегмент
|
# на живом роутере
|
||||||
ip rule add to 192.168.0.0/24 lookup main priority 100
|
ip rule add to 192.168.0.0/24 lookup main priority 100
|
||||||
# 2. та же строка в /etc/rc.local — иначе умрёт после ребута
|
|
||||||
# 3. firewall: Allow-upstream-LAN dest_ip += 192.168.0.0/24, добавить proto icmp
|
# и то же записано в /etc/rc.local (чтобы выжило ребут)
|
||||||
# 4. проверить, что masq зоны wan покрывает 192.168.0.0/24 (иначе ответ не вернётся)
|
```
|
||||||
|
|
||||||
|
Бэкап до правки: `/etc/rc.local.bak.20260917-132639`
|
||||||
|
|
||||||
|
Правила после правки (две строки priority 100 — это нормально):
|
||||||
|
|
||||||
|
```text
|
||||||
|
100: from all to 192.168.2.0/24 lookup main
|
||||||
|
100: from all to 192.168.0.0/24 lookup main
|
||||||
|
101: from all iif br-lan lookup vpn
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🔴 **Правка файлов на роутере — через скачивание → локальную правку → заливку.**
|
||||||
|
> `sed -i` по живому конфигу **запрещён** (и был заблокирован в этой сессии).
|
||||||
|
> Порядок: `scp` вниз → `patch` локально → `scp` наверх → `cat` для проверки.
|
||||||
|
|
||||||
|
### ⚠️ Что ОСТАЛОСЬ не сделано (осознанно, по объёму команды «ip rule»)
|
||||||
|
|
||||||
|
Alex дал команду только на `ip rule`. Следующие два пункта **не трогались**:
|
||||||
|
|
||||||
|
1. **NAT/masq для `192.168.0.0/24`** — нет. Пакет уходит с source `192.168.6.x`.
|
||||||
|
Проверка показала, что 80 порт всё равно отвечает (GPON, судя по всему, маршрутизирует
|
||||||
|
обратно), поэтому это **не блокер**, но и не гарантия.
|
||||||
|
2. **ICMP** — `Allow-upstream-LAN` в nft по-прежнему только `tcp`+`udp`, без `icmp`.
|
||||||
|
Значит **пинг из LAN в `192.168.0.0/24` не работает до сих пор**, хотя TCP/UDP проходит.
|
||||||
|
Не путать «не пингуется» с «не работает».
|
||||||
|
|
||||||
|
### План ремонта (исходный, для истории)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 1. ip rule — расширить bypass на GPON-сегмент ← СДЕЛАНО
|
||||||
|
ip rule add to 192.168.0.0/24 lookup main priority 100
|
||||||
|
# 2. та же строка в /etc/rc.local ← СДЕЛАНО
|
||||||
|
# 3. firewall: Allow-upstream-LAN dest_ip += 192.168.0.0/24, добавить proto icmp ← НЕ сделано
|
||||||
|
# 4. проверить, что masq зоны wan покрывает 192.168.0.0/24 ← НЕ сделано
|
||||||
```
|
```
|
||||||
|
|
||||||
Порядок работ по боевому конфигу: коммит ДО → правка → заливка → проверка Алекса руками → коммит ПОСЛЕ.
|
Порядок работ по боевому конфигу: коммит ДО → правка → заливка → проверка Алекса руками → коммит ПОСЛЕ.
|
||||||
|
|
||||||
Три причины: (1) `ip rule 100` покрывает только `192.168.2.0/24` — трафик на `192.168.0.0/24` не матчится и уходит в `table vpn`/`tun0` вместо WAN; (2) нет NAT/masq для GPON-сегмента — пакет уходит с source `192.168.6.x`, ответ не может вернуться; (3) `Allow-upstream-LAN` создан только для `tcp`+`udp`, **без `icmp`** — ICMP из LAN падает в KillSwitch, и роутер сам отдаёт `Destination Port Unreachable`. Поэтому **пинг не индикатор**: для `192.168.0.0/24` он врёт так же, как для `192.168.2.0/24`. Проверять только тем каналом, которым будете работать (SSH/TCP), напр. `nc -zv 192.168.0.50 80` или `ssh -o BatchMode=yes`.
|
|
||||||
|
|
||||||
## Downstream LAN Access
|
## Downstream LAN Access
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -765,6 +799,10 @@ Firewall backup from Cloudflare direct-bypass change:
|
|||||||
/etc/config/firewall.bak.20260714-195530
|
/etc/config/firewall.bak.20260714-195530
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Роутинг через туннель (`tunvpn`) — 2026-07-14.
|
||||||
|
Firewall/Cloudflare bypass — 2026-07-14.
|
||||||
|
GPON-транзит (`ip rule` на `192.168.0.0/24`) — 2026-09-17.
|
||||||
|
|
||||||
Rollback example:
|
Rollback example:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -783,6 +821,18 @@ uci commit dhcp
|
|||||||
/etc/init.d/dnsmasq restart
|
/etc/init.d/dnsmasq restart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Rollback GPON-транзита (2026-09-17):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# живая система
|
||||||
|
ip rule del to 192.168.0.0/24 lookup main priority 100
|
||||||
|
# постоянство
|
||||||
|
cp /etc/rc.local.bak.20260917-132639 /etc/rc.local
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ Правки `/etc/rc.local` на роутере делать **только** через `scp`-вниз → `patch` локально →
|
||||||
|
> `scp`-наверх. `sed -i` по живому конфигу запрещён.
|
||||||
|
|
||||||
## Notes And Pitfalls
|
## Notes And Pitfalls
|
||||||
|
|
||||||
- The Cloudflare bypass depends on Mac `cloudflared` using QUIC. If the Mac falls back to HTTP/2/TCP/443, the router exception will not match.
|
- The Cloudflare bypass depends on Mac `cloudflared` using QUIC. If the Mac falls back to HTTP/2/TCP/443, the router exception will not match.
|
||||||
@@ -792,8 +842,8 @@ uci commit dhcp
|
|||||||
- The firewall allow alone would not be sufficient, because policy routing rule `101` sends LAN ingress to `table vpn`. The direct `ip rule`s at priorities `98` and `99` must remain before priority `101`.
|
- The firewall allow alone would not be sufficient, because policy routing rule `101` sends LAN ingress to `table vpn`. The direct `ip rule`s at priorities `98` and `99` must remain before priority `101`.
|
||||||
- Rule order in `forward_lan` matters: Cloudflare direct allow must stay above `KillSwitch`.
|
- Rule order in `forward_lan` matters: Cloudflare direct allow must stay above `KillSwitch`.
|
||||||
- `eth0` is the **active** WAN as of 2026-09-17 (`192.168.2.157/24`, gw `192.168.2.2`); `usb0` is idle. At the 2026-07-22 capture this was inverted (`eth0` down, `usb0` carrying the default route). Do not trust the old banner — re-read `ip route` before assuming which WAN is live.
|
- `eth0` is the **active** WAN as of 2026-09-17 (`192.168.2.157/24`, gw `192.168.2.2`); `usb0` is idle. At the 2026-07-22 capture this was inverted (`eth0` down, `usb0` carrying the default route). Do not trust the old banner — re-read `ip route` before assuming which WAN is live.
|
||||||
- 🔴 **Транзит из LAN `192.168.6.0/24` в GPON-сегмент `192.168.0.0/24` НЕ работает (2026-09-17).** Причин три: `ip rule 100` не покрывает `192.168.0.0/24`; нет NAT/masq для этого направления; ICMP не разрешён в `Allow-upstream-LAN`. План ремонта — в одноимённом разделе. **Пока не применён.**
|
- 🔴 **Транзит из LAN `192.168.6.0/24` в GPON-сегмент `192.168.0.0/24` — ЧАСТИЧНО ПОЧИНЕН (2026-09-17).** `ip rule` добавлено (живая система + `/etc/rc.local`), Alex проверил 80 порт на `192.168.0.50` — работает. Было три причины: `ip rule 100` не покрывал `192.168.0.0/24` (✅ исправлено); нет NAT/masq для этого направления (❌ не сделано, не блокер); ICMP не разрешён в `Allow-upstream-LAN` (❌ не сделано). Подробности — в разделе «Транзит Rasputin → 192.168.0.0/24».
|
||||||
- 🔴 **ICMP не является индикатором доступности `192.168.0.0/24` — и подавно.** `Allow-upstream-LAN` создан только для `tcp`+`udp`, поэтому эхо-запросы из LAN падают в `KillSwitch` и роутер отвечает `Destination Port Unreachable` от себя. Проверять TCP-портом (`nc -zv 192.168.0.50 80`) или SSH.
|
- 🔴 **ICMP по-прежнему не индикатор доступности `192.168.0.0/24`.** `Allow-upstream-LAN` создан только для `tcp`+`udp`, поэтому эхо-запросы из LAN падают в `KillSwitch` и роутер отвечает `Destination Port Unreachable` от себя. **Пинг в GPON-сегмент не работает даже после починки `ip rule`** — TCP/UDP при этом проходит. Проверять TCP-портом (`nc -zv 192.168.0.50 80`) или SSH.
|
||||||
- Различай два похожих симптома: `Destination Port Unreachable` **от роутера `192.168.6.1`** = локальное отбивание ICMP/firewall-ом, а не закрытый порт на хосте. Признак живости — ответ на TCP/SSH, а не текст ICMP-ошибки.
|
- Различай два похожих симптома: `Destination Port Unreachable` **от роутера `192.168.6.1`** = локальное отбивание ICMP/firewall-ом, а не закрытый порт на хосте. Признак живости — ответ на TCP/SSH, а не текст ICMP-ошибки.
|
||||||
- 🔴 **Ping from Mac to an unreachable `192.168.2.x` host returns `Destination Port Unreachable` from `rasputin.lan (192.168.6.1)`, NOT a plain timeout.** This is the router rejecting the attempt — it does **not** mean the host is up with a closed port. Use `arp (incomplete)` + `100% packet loss` as the liveness signal. Confirmed 2026-09-15 (see the dedicated section above).
|
- 🔴 **Ping from Mac to an unreachable `192.168.2.x` host returns `Destination Port Unreachable` from `rasputin.lan (192.168.6.1)`, NOT a plain timeout.** This is the router rejecting the attempt — it does **not** mean the host is up with a closed port. Use `arp (incomplete)` + `100% packet loss` as the liveness signal. Confirmed 2026-09-15 (see the dedicated section above).
|
||||||
- Liveness of `192.168.2.0/24` hosts is best verified with the channel you intend to work through: key-based SSH (`ssh -o BatchMode=yes root@192.168.2.176 'uptime'`). Ping and web ports can mislead; an answered SSH session cannot.
|
- Liveness of `192.168.2.0/24` hosts is best verified with the channel you intend to work through: key-based SSH (`ssh -o BatchMode=yes root@192.168.2.176 'uptime'`). Ping and web ports can mislead; an answered SSH session cannot.
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ updated: '2026-09-17g'
|
|||||||
| **Конфиги** | `zont_config/` — свежие · `zont_config/archive/` — историчные |
|
| **Конфиги** | `zont_config/` — свежие · `zont_config/archive/` — историчные |
|
||||||
| **Типы объектов** | [[family/tech/zont-config-object-types]] |
|
| **Типы объектов** | [[family/tech/zont-config-object-types]] |
|
||||||
| **ZONT в общем контуре** | [[family/how-to/home-automation]] §6 |
|
| **ZONT в общем контуре** | [[family/how-to/home-automation]] §6 |
|
||||||
|
| **Снять живой конфиг с прибора** | 🔴 `curl -s http://192.168.0.50/config.txt` — **без авторизации**, формат ровно как у парсера. См. [[family/tech/zont-api]] §6bis |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -214,6 +215,8 @@ cd /Users/admin/Automation/HA-ZONT-Modbus
|
|||||||
|
|
||||||
# 1. Снять текущий конфиг с контроллера → в zont_config/
|
# 1. Снять текущий конфиг с контроллера → в zont_config/
|
||||||
# имя файла: config_<SN>_<SN>_<YYYY-MM-DD_HH-MM-SS>.txt
|
# имя файла: config_<SN>_<SN>_<YYYY-MM-DD_HH-MM-SS>.txt
|
||||||
|
# ✅ Напрямую с контроллера, без авторизации (см. §5g):
|
||||||
|
curl -s http://192.168.0.50/config.txt -o zont_config/config_$(date +%Y-%m-%d_%H-%M-%S).txt
|
||||||
|
|
||||||
# 2. TXT → YAML
|
# 2. TXT → YAML
|
||||||
python3 config-to-yml.py zont_config/config_XXXX.txt > /tmp/zont.yml
|
python3 config-to-yml.py zont_config/config_XXXX.txt > /tmp/zont.yml
|
||||||
@@ -306,7 +309,7 @@ diff A.txt B.txt # пусто = round-trip чистый
|
|||||||
| 5 | Неизвестный тип объекта → exit 2, файл не создаётся | Смотреть stderr — там причина |
|
| 5 | Неизвестный тип объекта → exit 2, файл не создаётся | Смотреть stderr — там причина |
|
||||||
| 6 | `validate_config()` → exit 4 | Печатает `❌ VALIDATION ERRORS` + список причин; файл намеренно **не выдан** (вывод пустой) |
|
| 6 | `validate_config()` → exit 4 | Печатает `❌ VALIDATION ERRORS` + список причин; файл намеренно **не выдан** (вывод пустой) |
|
||||||
| 7 | Регистр без своего устройства / аналоговый выход с битой ссылкой | WARNING в stderr, конвертация продолжается — проверить ссылки вручную |
|
| 7 | Регистр без своего устройства / аналоговый выход с битой ссылкой | WARNING в stderr, конвертация продолжается — проверить ссылки вручную |
|
||||||
| 8 | Загрузка конфига в контроллер — **руками**, скрипты только конвертируют | Конвертер не имеет доступа к ZONT |
|
| 8 | Загрузка конфига в контроллер — **руками**, скрипты только конвертируют | Конвертер не имеет доступа к ZONT. ⚠️ **Снятие конфига — уже не ручное:** `http://192.168.0.50/config.txt` отдаёт весь конфиг без авторизации (§5g) |
|
||||||
| 9 | `INFRASTRUCTURE.md`, `docker-compose.yml`, `docker run.txt` в проекте — **исторический TrueNAS-стек** | Актуальный контур — [[family/how-to/home-automation]]. Не искать `modbus-bridge`/`mbusd` на NAS |
|
| 9 | `INFRASTRUCTURE.md`, `docker-compose.yml`, `docker run.txt` в проекте — **исторический TrueNAS-стек** | Актуальный контур — [[family/how-to/home-automation]]. Не искать `modbus-bridge`/`mbusd` на NAS |
|
||||||
| 10 | Дополнительных зависимостей нет | Только `pyyaml` — `jsonschema`/`ruamel` не нужны |
|
| 10 | Дополнительных зависимостей нет | Только `pyyaml` — `jsonschema`/`ruamel` не нужны |
|
||||||
| 11 | ✅ **ИСПРАВЛЕНО 2026-09-17** — Сценарий с >1 шагом падал (exit 2) | `config-to-yml.py` теперь цикл по всем шагам; `yml-to-config.py` пишет все `step_ids` |
|
| 11 | ✅ **ИСПРАВЛЕНО 2026-09-17** — Сценарий с >1 шагом падал (exit 2) | `config-to-yml.py` теперь цикл по всем шагам; `yml-to-config.py` пишет все `step_ids` |
|
||||||
@@ -498,6 +501,102 @@ scenarios:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 5g. 🔴 Локальный эндпоинт контроллера: `http://192.168.0.50/config.txt`
|
||||||
|
|
||||||
|
**Факт (Alex, 2026-09-17):** контроллер отдаёт **весь конфиг** по HTTP **без авторизации**.
|
||||||
|
|
||||||
|
```
|
||||||
|
http://192.168.0.50/config.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| **Что отдаёт** | весь конфиг — тот же формат `#Z…` / `#S…`, что и файл из облака |
|
||||||
|
| **Авторизация** | **не требуется** |
|
||||||
|
| **Адрес** | `192.168.0.50` — локальный IP контроллера |
|
||||||
|
|
||||||
|
**Что это меняет:** снятие конфига с контроллера перестаёт быть ручной операцией через облако/UI —
|
||||||
|
его можно тянуть напрямую curl'ом из локальной сети.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# снять конфиг напрямую с контроллера
|
||||||
|
curl -s http://192.168.0.50/config.txt -o zont_config/config_$(date +%Y-%m-%d_%H-%M-%S).txt
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Не проверено:** кодировка ответа (ожидается windows-1251, как в облачных выгрузках) и коды
|
||||||
|
> ответа при недоступности. Проверить `file`/`iconv` перед тем, как полагаться на байтовую идентичность.
|
||||||
|
>
|
||||||
|
> ⚠️ **Запись через этот эндпоинт не подтверждена.** Известно только чтение. Загрузка конфига в
|
||||||
|
> контроллер — по-прежнему руками (см. §5b питфолл 8). Не предполагать наличие POST.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5h. 🌐 API ZONT — что доступно (исследовано 2026-09-17)
|
||||||
|
|
||||||
|
Доки скачаны в проект: `zont_api_docs/` (`zont_api_docs.html` 250 KB, `zont_api_docs.txt`,
|
||||||
|
`convert.py` — воспроизводимая HTML→text сборка).
|
||||||
|
|
||||||
|
**Источник:** https://zont-online.ru/api/docs/
|
||||||
|
|
||||||
|
### 🔴 Главный вывод: конфиг через API недоступен
|
||||||
|
|
||||||
|
Публичное API — это **состояния и история**, не конфигурация.
|
||||||
|
|
||||||
|
- Слово `scenario` в доке — **0 вхождений**. `relay` — 1.
|
||||||
|
- Ни одного метода для типов `11 / 14 / 46 / 49` (сценарии, реле, шаги, условия).
|
||||||
|
- `z3k_config` (где живут Z-объекты) упоминается **только как ссылка на ID** в `load_data`.
|
||||||
|
Метода для работы с ним **нет**.
|
||||||
|
|
||||||
|
**Следствие:** конвертер `.txt ⇄ .yml` — **единственный** путь правки сценариев и реле.
|
||||||
|
Переписать управление на API нельзя, переписывать нечего.
|
||||||
|
|
||||||
|
### Все методы API (11 шт.)
|
||||||
|
|
||||||
|
| Метод | Что делает | Конфиг? |
|
||||||
|
|---|---|---|
|
||||||
|
| `devices` | читает устройства + настройки (`load_io: true` → ещё и состояния) | чтение |
|
||||||
|
| `update_device` | **меняет настройки** — термостат, режимы отопления, расписание, доверенные номера, баланс SIM, OpenTherm, `custom_controls` | ⚠️ узко |
|
||||||
|
| `set_io_port` | управления состоянием: `guard-state`, `siren`, `engine-block`, `webasto` | — |
|
||||||
|
| `send_custom_command` | послать пользовательскую команду по `command_id` (ZTC/Mega SX) | — |
|
||||||
|
| `load_data` | **история** за период: `temperature`, `events`, `z3k_*` (Delta-time Array) | — |
|
||||||
|
| `raw_events` | история событий | — |
|
||||||
|
| `temperature` / `thermostat_work` | показания датчиков / работа термостата | — |
|
||||||
|
| `custom_controls` | история пользовательских статусов | — |
|
||||||
|
| `ztc_state` | питание, GSM, Wi-Fi | — |
|
||||||
|
| `generate_archive` / `download_generated_archive` | выгрузка архива | — |
|
||||||
|
|
||||||
|
**Наш H-2000 PRO** из `update_device` получает только **отопление**: `thermostat_mode`,
|
||||||
|
`thermostat_mode_temps`, `thermostat_ext_mode`, `thermostat_ext_modes_config`, `tempschedule`,
|
||||||
|
`thermometers`, `ot_*` (OpenTherm).
|
||||||
|
|
||||||
|
### Аутентификация
|
||||||
|
|
||||||
|
1. `get_authtoken` (POST, с login/password) → `token`
|
||||||
|
2. Дальше — заголовок `X-ZONT-Token: <token>` (или Basic)
|
||||||
|
3. Обязательный заголовок `X-ZONT-Client: <email>`
|
||||||
|
4. На **403** — токен отозван, получать заново
|
||||||
|
|
||||||
|
Базовый URL: `https://my.zont.online/api/<метод>`
|
||||||
|
|
||||||
|
**Легковесный вариант:** https://my.zont.online/api/widget/v3
|
||||||
|
|
||||||
|
### Что это значит практически
|
||||||
|
|
||||||
|
| Задача | Как решать |
|
||||||
|
|---|---|
|
||||||
|
| Править сценарии, реле, датчики | **только конвертер** (типы 11/14/46/49) |
|
||||||
|
| Снять конфиг | `http://192.168.0.50/config.txt` без авторизации (§5g) |
|
||||||
|
| Залить конфиг | руками (UI/облако) — API записи конфига нет |
|
||||||
|
| Смотреть состояния реле/датчиков онлайн | `devices?load_io=true` — **можно** |
|
||||||
|
| История температур/событий | `load_data` — **можно** |
|
||||||
|
| Менять режимы отопления | `update_device` — **можно** |
|
||||||
|
|
||||||
|
**Рекомендация:** конвертер не выкидывать — он единственный путь к сценариям. API-обвязка
|
||||||
|
(мониторинг состояний + история + режимы отопления) — **отдельная задача**, которая
|
||||||
|
**дополняет** конвертер, а не заменяет его.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 6. Состояние проекта (проверено 2026-09-17, вечер)
|
## 6. Состояние проекта (проверено 2026-09-17, вечер)
|
||||||
|
|
||||||
✅ **Конвертеры закоммичены — `199f2b1`.** Рабочее дерево чистое (после этого коммита).
|
✅ **Конвертеры закоммичены — `199f2b1`.** Рабочее дерево чистое (после этого коммита).
|
||||||
|
|||||||
+136
-1
@@ -31,6 +31,10 @@ updated: '2026-09-17'
|
|||||||
> **Итог одной строкой:** облачный API ZONT **не работает с конфигом** (сценарии, реле, шаги,
|
> **Итог одной строкой:** облачный API ZONT **не работает с конфигом** (сценарии, реле, шаги,
|
||||||
> условия). Конвертер `.txt ⇄ .yml` — по-прежнему **единственный** путь к правке сценариев.
|
> условия). Конвертер `.txt ⇄ .yml` — по-прежнему **единственный** путь к правке сценариев.
|
||||||
> API годится только для **мониторинга** и узкого набора настроек (отопление).
|
> API годится только для **мониторинга** и узкого набора настроек (отопление).
|
||||||
|
>
|
||||||
|
> ⚠️ **Уточнено 2026-09-17 (см. §6bis):** это верно для *облачного* API. **Локальный**
|
||||||
|
> интерфейс контроллера конфиг отдаёт — причём `GET /config.txt` **вообще без авторизации**,
|
||||||
|
> в формате нашего парсера. Прошивка локально не обновляется.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -188,7 +192,8 @@ POST https://my.zont.online/api/load_data
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **1. Конвертер `.txt ⇄ .yml`** (текущий) | правка сценариев, реле, датчиков, Modbus | загрузка в контроллер **руками** |
|
| **1. Конвертер `.txt ⇄ .yml`** (текущий) | правка сценариев, реле, датчиков, Modbus | загрузка в контроллер **руками** |
|
||||||
| **2. API-обвязка (дополнение)** | онлайн-состояния (`devices?load_io=true`), история (`load_data`), отопление (`update_device`), сирена/охрана (`set_io_port`) | сценарии **недоступны** |
|
| **2. API-обвязка (дополнение)** | онлайн-состояния (`devices?load_io=true`), история (`load_data`), отопление (`update_device`), сирена/охрана (`set_io_port`) | сценарии **недоступны** |
|
||||||
| **3. «Переделать с 0»** | ❌ не существует API, на который переписать |
|
| **3. Локальный интерфейс (§6bis)** | 🔴 `GET /config.txt` — **весь конфиг без авторизации**, в формате парсера | только чтение; запись — WS `#S<n>=` + `#S15=1` |
|
||||||
|
| **4. «Переделать с 0»** | ❌ не существует API, на который переписать | |
|
||||||
|
|
||||||
### Рекомендация (доложена Alex, решения пока нет)
|
### Рекомендация (доложена Alex, решения пока нет)
|
||||||
|
|
||||||
@@ -201,6 +206,132 @@ POST https://my.zont.online/api/load_data
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 6bis. 🟢 ЛОКАЛЬНЫЙ интерфейс контроллера — вот где реально есть конфиг
|
||||||
|
|
||||||
|
> **Исследовано 2026-09-17** (вторая половина того же дня, после починки транзита
|
||||||
|
> Rasputin → `192.168.0.0/24`, см. [[family/how-to/rasputin-router]]).
|
||||||
|
>
|
||||||
|
> **Итог одной строкой:** облачный API конфига не даёт (§3), но **локальный HTTP/WS интерфейс
|
||||||
|
> самого контроллера отдаёт ВЕСЬ конфиг**, причём `config.txt` — **вообще без авторизации**.
|
||||||
|
|
||||||
|
Это меняет вывод §6: путь «снять конфиг с прибора» больше не требует настроечной утилиты
|
||||||
|
и ручного экспорта — достаточно `curl`.
|
||||||
|
|
||||||
|
### Адрес и доступ
|
||||||
|
|
||||||
|
| Параметр | Значение |
|
||||||
|
|---|---|
|
||||||
|
| Хост | `192.168.0.50` (GPON-сегмент, за `192.168.0.1`) |
|
||||||
|
| Модель | `H2000_PRO`, прошивка `723 678` (`#S7`) |
|
||||||
|
| Версия веб-интерфейса | `25.11.1` (видна в `.version` при заходе в «Сервис») |
|
||||||
|
| Логин | `admin` |
|
||||||
|
| Пароль | `1316261` (сервисный) |
|
||||||
|
| WebSocket | `ws://192.168.0.50/ws` |
|
||||||
|
|
||||||
|
> 🔴 **Пароль локального UI — НЕ пароль от аккаунта `my.zont.online`.** Официальная дока
|
||||||
|
> (support.microline.ru → «Локальное управление приборами ZONT»):
|
||||||
|
> логин = «Имя пользователя» из `Настройки → Пользователи`; пароль = «**Пароль для управления
|
||||||
|
> с другого телефонного номера**». Для универсальных контроллеров в заводской конфигурации
|
||||||
|
> пользователя **нет вообще** — тогда авторизация не требуется.
|
||||||
|
|
||||||
|
### 🔴 `GET /config.txt` — весь конфиг без авторизации
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://192.168.0.50/config.txt -o zont-config-live.txt # 623 строки
|
||||||
|
```
|
||||||
|
|
||||||
|
Отдаёт **ровно тот формат `#S`/`#Z`**, который парсит `config-to-yml.py`
|
||||||
|
(см. [[family/how-to/zont-config-compiler]]). Ни логина, ни токена не нужно.
|
||||||
|
|
||||||
|
Остальные испытанные HTTP-пути → **404**: `/api`, `/config`, `/config.json`, `/backup`,
|
||||||
|
`/download`, `/firmware`, `/update`, `/upgrade`, `/fw.bin`, `/ota`, `/flash`, `/log`,
|
||||||
|
`/status`, `/users`, `/export`, `/import`, `/sdcard`, `/cgi-bin/*`, `/z3k_config`.
|
||||||
|
|
||||||
|
### Протокол WebSocket
|
||||||
|
|
||||||
|
Вход (виден в JS страницы, воспроизведён вручную):
|
||||||
|
|
||||||
|
```json
|
||||||
|
→ {"user":"admin","pass":"1316261"}
|
||||||
|
← {"auth":200} // 401 при неверной паре
|
||||||
|
```
|
||||||
|
|
||||||
|
Дальше три вида команд:
|
||||||
|
|
||||||
|
| Команда | Что делает | Пример ответа |
|
||||||
|
|---|---|---|
|
||||||
|
| `{"req_ids":N}` | список id объектов класса N | `{"ids":[8560,8669,9877,…]}` |
|
||||||
|
| `{"scmd":"#S<n>?"}` | **чтение** любой системной настройки | `{"scmdres":"#S217:mqtt://…"}` |
|
||||||
|
| `{"scmd":"#S<n>=<val>"}` | **запись** настройки | `{"cmdres":0}` (0 = ок) |
|
||||||
|
| `{"scmd":"#S15=1"}` | 🔴 **СОХРАНИТЬ** изменения | — |
|
||||||
|
| `{"id":N,"cmd":M}` | команда объекту (реле, режим) | — |
|
||||||
|
| `{"id":N,"req_state":0}` | запросить состояние объекта | — |
|
||||||
|
|
||||||
|
Классы для `req_ids` (из JS): `16` отопит. контуры, `20` режимы отопления, `1` виртуальные
|
||||||
|
датчики, `27` датчики температуры, `0` дискретные датчики, `8` радиодатчики, `10` GUI-элементы,
|
||||||
|
`6` адаптеры.
|
||||||
|
|
||||||
|
> 🔴 **Неявная кнопка «сохранить» — `#S15=1`.** UI шлёт её после смены Wi-Fi-настроек
|
||||||
|
> (`#S208='ssid','pass'` + `#S15=1` + через 3 с `#S208?` для проверки). Без неё запись
|
||||||
|
> не применяется. В веб-морде этой кнопки нет.
|
||||||
|
|
||||||
|
### 25 настроек `#S` в живом конфиге — UI показывает только 9
|
||||||
|
|
||||||
|
Веб-интерфейс показывает `#S7`, `#S200`, `#S202`, `#S204`, `#S208`, `#S54`, `#S198`, `#S205`,
|
||||||
|
`SDATE`. В конфиге их **25**. Неявные:
|
||||||
|
|
||||||
|
| Ключ | Значение (живое, 2026-09-17) | Что это |
|
||||||
|
|---|---|---|
|
||||||
|
| `#S217` | `mqtt://zont:mqtt1z3$@192.168.0.10:1883` | MQTT-брокер **с паролем в открытом виде** |
|
||||||
|
| `#S218` | `'zont','qwertyui'` | MQTT логин/пароль |
|
||||||
|
| `#S219` | `'zont'` | MQTT клиент-id |
|
||||||
|
| `#S221` | `homeassistant` | префикс discovery |
|
||||||
|
| `#S124` | `1,9600,0,0` | параметры Modbus-шины (slave, baud, …) |
|
||||||
|
| `#S201` | `52200` | порт облачного сервера |
|
||||||
|
| `#S203` | `auto` | режим выбора канала связи |
|
||||||
|
| `#S12` | `*100# 0` | USSD-запрос баланса |
|
||||||
|
| `#S210` | `2147483647,2147483647,2147483647,2147483647` | лимиты |
|
||||||
|
| `#S207` | `+780****7291 0` | доверенный номер |
|
||||||
|
| `#S36`, `#S206`, `#S209`, `#S211`, `#S212`, `#S214`, `#S222`, `#S223`, `#S225`, `#S226` | — | нигде не задокументированы |
|
||||||
|
|
||||||
|
### Прошивка — локально её НЕТ
|
||||||
|
|
||||||
|
- Все HTTP-пути к прошивке → **404** (список выше).
|
||||||
|
- Кнопка «Обновить интерфейс» в разделе «Сервис» — это **перезагрузка веб-страницы**, не прошивка.
|
||||||
|
- Прошивка обновляется **только через облако ZONT** либо **настроечной утилитой с ПК**.
|
||||||
|
Локальный веб-UI загрузку `.bin` не умеет.
|
||||||
|
- Версии: модель/прошивка читаются из `#S7` (`H2000_PRO 723 678`), серийник — из `#S54`
|
||||||
|
(`0FA7C33CC89F`).
|
||||||
|
|
||||||
|
### 🔴 Утечка секретов
|
||||||
|
|
||||||
|
В `config.txt` **без авторизации** лежат: пароль облака ZONT (`#S202` — серийник +
|
||||||
|
`ktqs42047wd58t0i`), пароль MQTT (`#S217`/`#S218`), и **сервисный пароль локального UI**
|
||||||
|
(`#Z8192=4,'admin','+792****5401,…','1316261',0`). Любой, кто дотянется до `192.168.0.50`,
|
||||||
|
получает их без входа.
|
||||||
|
|
||||||
|
> Практический смысл: доступ к GPON-сегменту = доступ ко всем учёткам контроллера.
|
||||||
|
> Транзит из `192.168.6.0/24` был сломан ([[family/how-to/rasputin-router]]) — это,
|
||||||
|
> по иронии, единственное, что закрывало эти данные от домашней LAN.
|
||||||
|
|
||||||
|
### Инструменты разведки (на Mac, проект не трогают)
|
||||||
|
|
||||||
|
| Файл | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `~/rasputin-tmp/zont-auth-probe.js` | проверка пары логин/пароль (`node zont-auth-probe.js <host> <user> <pass>`) |
|
||||||
|
| `~/rasputin-tmp/zont-recon.js` | перебор HTTP-путей |
|
||||||
|
| `~/rasputin-tmp/zont-recon2.js` | перебор путей прошивки/конфига (печатает только не-404) |
|
||||||
|
| `~/rasputin-tmp/zont-ws-probe.js` | WS: auth + `req_ids` + чтение `#S` |
|
||||||
|
| `~/rasputin-tmp/zont-config-live.txt` | живой дамп `config.txt` (623 строки) |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/rasputin-tmp
|
||||||
|
node zont-auth-probe.js 192.168.0.50 admin 1316261 # → AUTH OK (200)
|
||||||
|
node zont-ws-probe.js ws://192.168.0.50/ws admin 1316261
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 7. Питфоллы
|
## 7. Питфоллы
|
||||||
|
|
||||||
| # | Питфолл | Как обойти |
|
| # | Питфолл | Как обойти |
|
||||||
@@ -213,6 +344,9 @@ POST https://my.zont.online/api/load_data
|
|||||||
| 6 | DTA — вторая и последующие метки **отрицательные** (смещения назад) | Не читать их как абсолютное время |
|
| 6 | DTA — вторая и последующие метки **отрицательные** (смещения назад) | Не читать их как абсолютное время |
|
||||||
| 7 | `update_device` при `403` — токен мог быть отозван | Перезапросить `get_authtoken` |
|
| 7 | `update_device` при `403` — токен мог быть отозван | Перезапросить `get_authtoken` |
|
||||||
| 8 | Дока **обновляется**, часть функций может отсутствовать | При нехватке метода — писать на `admin@zont.online` |
|
| 8 | Дока **обновляется**, часть функций может отсутствовать | При нехватке метода — писать на `admin@zont.online` |
|
||||||
|
| 9 | 🔴 Пароль локального UI — **не** пароль от `my.zont.online` | Это «Пароль для управления с другого телефонного номера» из `Настройки → Пользователи` (§6bis) |
|
||||||
|
| 10 | 🔴 Запись через WS **не применяется** без `{"scmd":"#S15=1"}` | Всегда досылать команду сохранения после записи `#S<n>=…` |
|
||||||
|
| 11 | 🔴 `GET /config.txt` отдаёт секреты **без авторизации** | Не считать GPON-сегмент доверенной сетью; см. §6bis |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -222,3 +356,4 @@ POST https://my.zont.online/api/load_data
|
|||||||
- [[family/tech/zont-scenario-logic-11109]] — структура сценариев 11/46/49/45
|
- [[family/tech/zont-scenario-logic-11109]] — структура сценариев 11/46/49/45
|
||||||
- [[family/tech/zont-config-object-types]] — таблица типов объектов конфига
|
- [[family/tech/zont-config-object-types]] — таблица типов объектов конфига
|
||||||
- [[family/how-to/home-automation]] — контур автоматизации, ZONT, Modbus (§6)
|
- [[family/how-to/home-automation]] — контур автоматизации, ZONT, Modbus (§6)
|
||||||
|
- [[family/how-to/rasputin-router]] — транзит в GPON-сегмент `192.168.0.0/24` (нужен для доступа к `192.168.0.50`)
|
||||||
|
|||||||
Reference in New Issue
Block a user