Files
obsidian-vault/family/how-to/nodered-ventilation.md
T

154 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 'Node-RED: вентиляция по CO₂'
tags:
- family
- home
- automation
- nodered
- ventilation
updated: '2026-09-14'
related:
- '[[family/plans/t610-home-automation]]'
- '[[family/how-to/home-automation]]'
---
> ## 🔄 ГДЕ ЖИВЁТ (обновлено 2026-09-14)
> **Flows перенесены на HP t610** (HA OS) — **перенос выполнен 2026-09-14**, см. [[family/plans/t610-home-automation]] §5-кватер-Г.
> - **Файл:** `/addon_configs/a0d7b954_nodered/flows.json` в аддоне `a0d7b954_nodered` (**68 узлов**, было 124 б пустых).
> - **🔴 Ключевая правка при переезде:** узел `server` — `"addon": false` → **`"addon": true`**. На TrueNAS Node-RED был отдельным docker-контейнером и ходил в HA по адресу + long-lived token (**токена в переносимых файлах нет вообще** — не искать). На t610 он HA-аддон → в аддон-режиме Supervisor даёт доступ к HA без токена. Лог после правки: `[server:Home Assistant] Connected to http://supervisor/core`, ошибок 0.
> - **Модуль:** опция аддона `npm_packages: ["node-red-contrib-home-assistant-websocket@0.80.3"]`.
> - **Доступ:** наружу НЕ выпущен (`host_network: true` глушит порт-маппинг; снимается только галочкой в UI) → **через ingress HA**: `http://192.168.2.176/api/hassio_ingress/4vUcCxEpYJMbQ64z-kWPCvFadq8ayl4lzvzJPDUaZzE/`. Домен `nodered.mallexxx.duckdns.org` **не используется** (отдаёт 401 от nginx HA).
> - **Старый Node-RED на TrueNAS** (`/mnt/RED_2TB/docker/nodered/`, порт 1880) пока жив как источник/откат.
> - **⚠️ Известное следствие:** узел `fan.fan_at2_1/2` (slave 10) в HA на t610 — `unavailable` (блок закомментирован в `configuration.yaml`), поэтому управление вентиляторами AT2 не работает. Также проверить наличие `cover.intake_damper_*` (в конфиге HA заслонки описаны как `switch.*`). Alex: задача по slave 10 **не ставилась**.
Node-RED flow для управления вентиляцией по данным CO₂ датчиков.
## Структура
```
[room demand msgs]
(1) Demand Aggregator
(2) Intake Allocation
(3) Discretization per damper
(4) Intake Damper Outputs
(5) Exhaust Arbitration
(6) Exhaust Dampers
(7) Exhaust Fans
(8) Kitchen Hood
```
## 1. Demand Aggregator (stateful)
```js
let demands = flow.get("room_demands") || {};
demands[msg.room] = msg.raw_demand;
flow.set("room_demands", demands);
msg.demands = demands;
return msg;
```
## 2. Intake Allocation
Комнаты с intake дамперами: bedroom, kids, living. Приоритеты: living = 1.2, остальные = 1.0.
```js
const ROOMS = ["bedroom", "kids", "living"];
const PRIORITY = { bedroom: 1.0, kids: 1.0, living: 1.2 };
let weighted = {}, sum = 0;
for (let r of ROOMS) {
let d = msg.demands[r] || 0;
weighted[r] = d * (PRIORITY[r] || 1);
sum += weighted[r];
}
if (sum === 0) return null;
let allocation = {};
for (let r of ROOMS) { allocation[r] = 100 * weighted[r] / sum; }
msg.intake_pct = allocation;
return msg;
```
## 3. Discretization
**Bedroom/Kids** (0 / 33 / 66 / 100):
```js
function snap(x) {
if (x < 10) return 0;
if (x < 33) return 33;
if (x < 66) return 66;
return 100;
}
msg.payload = snap(msg.payload);
return msg;
```
**Living (dual-damper)**:
```js
let p = msg.payload;
let A = 0, B = 0;
if (p < 10) { A=0; B=0; }
else if (p < 30) { A=66; B=0; }
else if (p < 55) { A=66; B=66; }
else if (p < 80) { A=100; B=66; }
else { A=100; B=100; }
msg.damperA = A; msg.damperB = B;
return msg;
```
## 5. Exhaust Arbitration
```js
let d = msg.demands;
msg.exhaust = {
at2_1: Math.max(d.toilet || 0, d.shower || 0, d.kitchen || 0),
at2_2: Math.max(d.bathroom || 0, d.office || 0)
};
return msg;
```
## 7. Fan Speed
```js
function speed(d) {
if (d < 5) return 30;
if (d < 20) return 50;
if (d < 40) return 70;
return 100;
}
msg.payload = speed(msg.payload);
return msg;
```
## 8. Kitchen Hood (3-speed)
```js
let d = msg.demands.kitchen || 0;
if (d < 10) msg.payload = 0;
else if (d < 30) msg.payload = 1;
else if (d < 60) msg.payload = 2;
else msg.payload = 3;
return msg;
```
Gate: current_state (manual override → block automation).
Все дамперы: rate-limit 1 msg / 3060 s.
## Связанные заметки
- [[family/how-to/home-automation]] — общая автоматизация дома
- [[family/plans/t610-home-automation]] — миграция на t610, §5-кватер-Г (перенос flows) и §5-кватер-З (фикс датчика столовой)
- [[Modbus/RTU_Framing_Source_Analysis]] — канон сборки RTU-кадров (почему `dining` молчал и как починили)
## Статус данных для автоматики (2026-09-14)
**Все три комнаты (`bedroom`, `kids`, `dining`) публикуют данные в MQTT.** Датчик столовой (`slave 1`) был сломан багом сборки кадров в `modbus-bridge` — починен 2026-09-14 (см. [[family/plans/t610-home-automation]] §5-кватер-З). До фикса автоматика вентиляции по CO₂ работала без данных столовой.
⚠️ **`fan.fan_at2_1/2` (slave 10) — `unavailable`** (блок закомментирован в `configuration.yaml`) → исполнительный контур вентиляторов AT2 не работает. Задача по slave 10 Alex'ом не ставилась.