Files
gree-controller/docs/original-spec.md
T
2026-08-23 21:34:07 +02:00

3085 lines
47 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.
Tak — i **to jest bardzo sensowny projekt**, szczególnie że nie musimy wymyślać protokołu od zera. GREE/EWPE ma lokalny protokół UDP, a obecna integracja Home Assistant również komunikuje się lokalnie. Oficjalna integracja HA jest typu *Local Polling* i obsługuje m.in. tryb HVAC, temperaturę, wentylator, swing i presety.
Co ważniejsze, są już implementacje pokazujące, że protokół można obsługiwać niezależnie od Home Assistanta: UDP/7000, szyfrowanie AES, discovery/bind/status/command. Nowsze implementacje obsługują nawet AES-128-ECB i AES-128-GCM.
### Ja zrobiłbym to trochę inaczej niż zwykły custom component
Zaprojektowałbym **własny lokalny serwer GREE + integrację HA**, np.:
```
```
```
┌──────────────────────────┐
│ Home Assistant │
│ │
│ climate.gree_salon │
│ sensor.gree_temperature │
│ switch.gree_quiet │
│ ... │
└────────────┬─────────────┘
REST / WebSocket
┌────────────▼─────────────┐
│ Gree Controller │
│ │
│ Device Manager │
│ State Manager │
│ Automation Engine │
│ Heating Manager │
│ Scheduler │
│ Web UI │
└────────────┬─────────────┘
GREE UDP / AES
┌─────────────────────┼─────────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ GREE AC │ │ GREE AC │ │ GREE AC │
│ salon │ │ sypial. │ │ biuro │
└─────────┘ └─────────┘ └─────────┘
```
Dzięki temu **HA nie jest miejscem, w którym znajduje się cała logika**. HA staje się klientem naszego kontrolera.
---
## Najważniejsze: kompatybilność 1:1
Chciałbym zachować praktycznie ten sam model urządzenia, który HA już zna:
### Climate
- `HVAC_MODE_OFF`
- `HVAC_MODE_AUTO`
- `HVAC_MODE_COOL`
- `HVAC_MODE_HEAT`
- `HVAC_MODE_DRY`
- `HVAC_MODE_FAN_ONLY`
- temperatura zadana
- temperatura pomieszczenia
- prędkość wentylatora
- swing pionowy
- swing poziomy
- preset
- power
Obecna integracja GREE obsługuje już te podstawowe funkcje.
### Switch
Np.:
- panel light
- quiet
- X-Fan
- turbo
- health
- sleep
- child lock
- display
- eco
Oczywiście tylko te, które faktycznie obsługuje konkretny model.
### Dodatkowe sensory
I tutaj możemy zrobić znacznie więcej:
```
```
```
sensor.gree_salon_temperature
sensor.gree_salon_humidity
sensor.gree_salon_power
sensor.gree_salon_mode
sensor.gree_salon_fan
sensor.gree_salon_connection
sensor.gree_salon_last_update
sensor.gree_salon_energy
```
oraz diagnostykę:
```
```
```
sensor.gree_salon_response_time
sensor.gree_salon_packets
sensor.gree_salon_errors
sensor.gree_salon_uptime
```
---
# Discovery
To też możemy zrobić porządnie.
Standardowo GREE wykorzystuje UDP/7000. Discovery może działać przez broadcast, a dla VLAN-ów możemy zrobić dodatkowo aktywne wykrywanie po podsieciach. Istniejące implementacje już stosują unicast UDP/7000 właśnie do urządzeń znajdujących się w innych VLAN-ach.
W naszym UI:
```
```
```
GREE Controller
────────────────────────────────
Devices
✓ GREE Salon
192.168.10.51
AA:BB:CC:DD:EE:01
Online
22.4°C
✓ GREE Sypialnia
192.168.10.52
AA:BB:CC:DD:EE:02
Online
20.8°C
[ Scan network ]
Network:
192.168.10.0/24
[ Scan ]
```
A po znalezieniu:
```
```
```
Found GREE device
Model: GREE ...
MAC: xx:xx:xx:xx:xx:xx
IP: 192.168.10.51
Protocol: GREE / EWPE
Encryption: AES-128-GCM
Firmware: ...
[ Add device ]
```
---
# I tutaj widzę bardzo ważną rzecz
Nie opierałbym projektu bezpośrednio na obecnym kodzie integracji HA.
Mamy już kilka projektów, które pokazują różne implementacje protokołu:
- Home Assistant GreeClimateComponent
- `gree-remote`
- implementacje EWPE
- implementacje Rust
- lokalne serwery GREE
Nawet istniejący projekt Rust ma już architekturę bardzo zbliżoną do tego, co chcemy osiągnąć: daemon rozmawia z klimatyzatorem przez UDP/7000, a frontend dostaje normalne dane JSON i aktualizacje na żywo.
**Ale nasz projekt powinien mieć własną warstwę protokołu.**
Czyli:
```
```
```
gree_protocol/
discovery
encryption
packet
bind
status
command
device_capabilities
```
nad tym:
```
```
```
gree_core/
Device
DeviceManager
StateManager
Scheduler
AutomationEngine
HeatingManager
```
nad tym:
```
```
```
api/
REST
WebSocket
```
oraz:
```
```
```
web/
dashboard
devices
automations
heating
schedules
diagnostics
```
i osobno:
```
```
```
homeassistant/
config_flow
climate
sensor
switch
binary_sensor
```
---
# Najciekawsza część: inteligentne ogrzewanie
Tutaj nasza aplikacja może być **dużo bardziej użyteczna niż zwykła integracja GREE**.
Przykład:
### Strefa: salon
```
```
```
Heating zone: Salon
Target:
21.5°C
Comfort:
21.5°C
Night:
19.0°C
Away:
17.0°C
Minimum:
16.0°C
```
Źródło temperatury:
```
```
```
○ GREE
● Xiaomi BLE sensor
○ Home Assistant sensor
○ średnia z kilku czujników
```
I wtedy kontroler decyduje:
```
```
```
22:00
Night mode
target = 19°C
06:30
Morning
target = 21°C
08:30
Nobody home
target = 18°C
16:00
Somebody home
target = 21°C
```
Ale możemy zrobić jeszcze lepiej.
---
# Histereza
Nie chcemy:
```
```
```
21.0 → ON
21.1 → OFF
21.0 → ON
21.1 → OFF
```
tylko:
```
```
```
target = 21°C
hysteresis = 0.4°C
ON < 20.6°C
OFF > 21.4°C
```
A dodatkowo:
```
```
```
minimum ON time
minimum OFF time
minimum interval between commands
```
żeby nie katować klimatyzatora.
---
# Predykcyjne ogrzewanie
To jest funkcja, którą naprawdę warto zrobić.
Jeżeli system zauważy:
```
```
```
18:00 19.2°C
18:15 19.7°C
18:30 20.1°C
18:45 20.5°C
19:00 20.8°C
```
może policzyć:
```
```
```
Heating rate:
+0.53°C / hour
```
i wiedzieć, że dojście do 21°C zajmie około:
```
```
```
23 min
```
Możemy więc ustawić:
> „O 19:00 chcę mieć 21°C”
i rozpocząć ogrzewanie odpowiednio wcześniej.
---
# Możemy też sterować wieloma klimatyzatorami
Przykładowo:
```
```
```
Heating Zone
┌─────────┴─────────┐
│ │
GREE Salon GREE Kuchnia
21°C 21°C
│ │
└─────────┬─────────┘
Temperature
21.2°C
```
System może zdecydować:
```
```
```
Salon:
HEAT 21°C
Kuchnia:
OFF
```
albo:
```
```
```
Salon:
HEAT 20.5°C
Kuchnia:
HEAT 20.5°C
```
---
# Automatyzacje
Zamiast pisać YAML:
```
```
```
trigger:
...
condition:
...
action:
...
```
robimy prosty GUI:
```
```
```
Automation
WHEN
[ Temperature ]
[ Salon ]
[ falls below ]
[ 20°C ]
AND
[ Time ]
[ 06:00 - 22:00 ]
THEN
[ Climate ]
[ Salon ]
[ Heat ]
[ 21°C ]
```
Albo:
```
```
```
WHEN
Outside temperature < 5°C
AND
Time = 22:00
THEN
Set heating zone "Salon"
Target = 19°C
Fan = Auto
```
---
# Live state
Web UI zrobiłbym na WebSocketach/SSE.
Na dashboardzie:
```
```
```
┌──────────────────────────────────────────┐
│ SALON │
│ │
│ 21.3 °C │
│ │
│ Target 21.0 °C │
│ │
│ ● HEATING │
│ │
│ Mode HEAT │
│ Fan AUTO │
│ Swing AUTO │
│ │
│ Power ███████████░ │
│ │
│ [ ] 21.0°C [ + ] │
└──────────────────────────────────────────┘
```
I **bez odświeżania strony**:
```
```
```
AC → Controller → WebSocket → Browser
```
---
# Historia
Możemy przechowywać:
```
```
```
temperature
target temperature
mode
fan
power
connection
commands
errors
```
i zrobić wykres:
```
```
```
22°C ┤ ╭────────
21°C ┤ ╭──────╯
20°C ┤ ╭────╯
19°C ┤───────╯
└────────────────────────────
12 14 16 18 20 22
```
Do tego:
```
```
```
Heating time today: 3h 42m
Commands: 27
Communication errors: 0
Estimated consumption: ...
```
---
# Bardzo ważna rzecz — odporność
To jest właśnie coś, co może być **lepsze od obecnej integracji HA**.
Nie chcemy:
```
```
```
HA
├── request
├── timeout
├── retry
└── error
```
tylko:
```
```
```
Controller
persistent state
┌─────────────┴─────────────┐
│ │
GREE AC Home Assistant
```
Controller cały czas monitoruje urządzenia.
Jeżeli AC zniknie:
```
```
```
ONLINE
TIMEOUT
RETRY 1
RETRY 2
OFFLINE
```
Po powrocie:
```
```
```
OFFLINE
DISCOVER/RECONNECT
GET STATE
ONLINE
```
HA dostaje już tylko poprawny stan.
---
# I jest jeszcze jedna bardzo istotna kwestia
**Nowsze firmware GREE faktycznie może być problemem.**
Znalazłem świeże zgłoszenie z lipca 2026 dotyczące GREE Clivia, gdzie autor raportuje, że na firmware 2.12+ urządzenie pozostaje pingowalne, ale nie odpowiada na UDP/7000, przez co standardowa integracja HA nie może wykonać discovery/bind.
To oznacza, że zanim zaczniemy pisać GUI, zrobiłbym **reverse engineering konkretnego dongla/klimatyzatora, którego masz**.
I to jest najlepszy pierwszy etap.
### Etap 1 — „GREE Protocol Lab”
Napiszemy mały program:
```
```
```
gree-probe
```
który:
```
```
```
1. nasłuchuje UDP
2. wyszukuje GREE
3. identyfikuje urządzenie
4. wykonuje discovery
5. próbuje bind
6. odczytuje capabilities
7. odczytuje status
8. wykonuje pojedyncze komendy
9. loguje wszystkie pakiety
10. zapisuje PCAP
```
Np.:
```
```
```
$ gree-probe scan
Found 2 devices
192.168.10.51
MAC: ...
Model: ...
Protocol: GREE
Encryption: AES-GCM
UDP: 7000
Status: OK
192.168.10.52
MAC: ...
Model: ...
Protocol: GREE
Encryption: AES-GCM
UDP: 7000
Status: OK
```
Potem:
```
```
```
gree-probe monitor 192.168.10.51
```
i dostajemy:
```
```
```
14:32:01 temp=21.4 target=21.0 mode=heat fan=auto
14:32:02 temp=21.4 target=21.0 mode=heat fan=auto
14:32:03 temp=21.5 target=21.0 mode=heat fan=auto
```
Dopiero na tym fundamencie budowałbym resztę.
**I zdecydowanie nie robiłbym tego jako kolejnego prostego custom componentu HA.** Zrobiłbym własny daemon/controller + oficjalny custom integration HA. Wtedy możesz mieć jednocześnie **pełną kompatybilność encji z HA, własny panel WWW, własny scheduler, inteligentne ogrzewanie, historię i automatyzacje**, a protokół GREE jest całkowicie odseparowany od Home Assistanta.
Jeżeli chcesz, możemy teraz przejść do **konkretnej architektury projektu i zacząć od reverse-engineeringu protokołu GREE — pliki, moduły, Python/Rust, API, baza danych i później gotowy custom integration dla HA**.
Plan jest na to absyntów było w rust z interfejsem i będzie to na osobnym hoście/ kontenerze lxc lub w integracji homeassistant i jakoś trzeba pogodzić przekazywanie encji, generalnie osobny kontener i środowisko to must have
Tak — w takim przypadku **zdecydowanie wybrałbym osobny kontener z usługą w Rust**, a integrację Home Assistant potraktowałbym wyłącznie jako cienkiego klienta. To daje nam izolację, własny lifecycle i możliwość rozwijania całego systemu niezależnie od HA.
Najważniejsze jest tylko dobre rozwiązanie kwestii encji.
## Docelowa architektura
```
```
```
┌─────────────────────────────┐
│ HOME ASSISTANT │
│ │
│ climate.gree_salon │
│ sensor.gree_salon_temp │
│ sensor.gree_salon_humidity │
│ switch.gree_salon_turbo │
│ ... │
└──────────────┬──────────────┘
Custom Integration
WebSocket + REST
┌──────────────▼──────────────┐
│ GREE CONTROLLER │
│ │
│ Rust │
│ │
│ ┌───────────────────────┐ │
│ │ GREE Protocol │ │
│ │ UDP / AES / discovery │ │
│ └───────────────────────┘ │
│ │
│ ┌───────────────────────┐ │
│ │ Device Manager │ │
│ └───────────────────────┘ │
│ │
│ ┌───────────────────────┐ │
│ │ Automation Engine │ │
│ └───────────────────────┘ │
│ │
│ ┌───────────────────────┐ │
│ │ Heating Manager │ │
│ └───────────────────────┘ │
│ │
│ ┌───────────────────────┐ │
│ │ REST API │ │
│ │ WebSocket │ │
│ └───────────────────────┘ │
│ │
│ ┌───────────────────────┐ │
│ │ Web UI │ │
│ └───────────────────────┘ │
└──────────────┬──────────────┘
LAN / VLAN
┌───────────────────┼───────────────────┐
│ │ │
GREE #1 GREE #2 GREE #3
```
### Kontener
Ja bym zrobił:
```
```
```
LXC
└── gree-controller
├── gree-controller
├── gree.db
└── config/
```
albo Docker/Podman, jeżeli później będziesz chciał łatwo przenosić usługę.
**Rust + jeden binarny executable** jest tutaj bardzo dobrym rozwiązaniem.
---
# Encje HA
Nie przekazywałbym encji z kontenera bezpośrednio.
To znaczy controller **nie powinien próbować udawać Home Assistanta**.
Zamiast tego:
```
```
```
GREE Controller
│ API
HA Custom Integration
HA Entity Registry
├── climate
├── sensor
├── switch
├── binary_sensor
└── number
```
Czyli:
**Rust zarządza urządzeniem, HA zarządza encją.**
To jest bardzo ważne rozdzielenie odpowiedzialności.
---
# Jak będzie wyglądała komunikacja
Najlepiej zrobić **WebSocket jako kanał realtime**, a REST jako konfigurację/command API.
### REST
```
```
```
GET /api/v1/devices
GET /api/v1/devices/{id}
GET /api/v1/devices/{id}/state
POST /api/v1/devices/{id}/commands
GET /api/v1/zones
GET /api/v1/automations
POST /api/v1/automations
GET /api/v1/history
```
### WebSocket
```
```
```
WS /api/v1/events
```
Controller wysyła:
```
```
```
{
"event": "device_state",
"device_id": "gree-salon",
"state": {
"power": true,
"mode": "heat",
"temperature": 21.4,
"target_temperature": 21.0,
"fan": "auto"
}
}
```
HA dostaje to natychmiast.
---
# Co z komendami?
HA:
```
```
```
climate.set_temperature
```
Custom integration:
```
```
```
POST /api/v1/devices/gree-salon/commands
```
Rust:
```
```
```
Command::SetTemperature(21.0)
```
GREE UDP
odpowiedź
Rust aktualizuje stan
WebSocket
HA
I wtedy HA nie musi samodzielnie rozmawiać z GREE.
---
# Ale zrobiłbym jeszcze jedną rzecz
**Stan powinien być przechowywany w controllerze.**
Czyli jeśli HA się zrestartuje:
```
```
```
HA
restart
integration connects
GET /api/v1/devices
GET current state
```
i natychmiast odtwarza encje.
A nie:
```
```
```
HA restart
czekamy aż GREE odpowie
discovery
bind
state
```
Controller już wie:
```
```
```
GREE Salon
last_state
last_seen
capabilities
configuration
```
---
# Discovery HA
Custom integration może działać bardzo elegancko.
Po instalacji:
```
```
```
Settings
→ Devices & services
→ Add integration
→ GREE Controller
```
Podajesz:
```
```
```
Host:
192.168.1.20
Port:
8765
```
Integration robi:
```
```
```
GET /api/v1/info
```
i:
```
```
```
GET /api/v1/devices
```
Dostaje:
```
```
```
{
"devices": [
{
"id": "gree-salon",
"name": "Salon",
"type": "air_conditioner",
"capabilities": [
"heat",
"cool",
"dry",
"fan",
"swing",
"turbo"
]
}
]
}
```
I tworzy encje.
---
# Co daje nam osobny Rust?
Ogromnie dużo.
### 1. HA nie musi znać protokołu GREE
Jeżeli GREE zmieni:
```
```
```
AES
UDP
bind
discovery
packet format
```
aktualizujemy controller.
HA integration pozostaje praktycznie bez zmian.
---
### 2. Możemy mieć własny Web UI
I to będzie **pełnoprawny panel**, nie dodatek do HA.
Np.:
```
```
```
Dashboard
┌───────────────────────────────────────────────┐
│ GREE CONTROLLER │
│ │
│ 3 devices 3 online 0 errors │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ SALON │ │ SYPIALNIA │ │
│ │ │ │ │ │
│ │ 21.4°C │ │ 20.1°C │ │
│ │ → 21.0°C │ │ → 19.0°C │ │
│ │ │ │ │ │
│ │ HEAT │ │ HEAT │ │
│ └──────────────┘ └──────────────┘ │
└───────────────────────────────────────────────┘
```
---
# 3. Automatyzacje nie muszą zależeć od HA
I to jest moim zdaniem **najciekawsza rzecz**.
Możemy zrobić:
```
```
```
GREE Controller
├── automation engine
├── heating engine
├── scheduler
└── HA integration
```
Czyli np. ogrzewanie działa nawet kiedy HA jest wyłączony.
---
# Ale wtedy potrzebujemy integracji z HA także w drugą stronę
To znaczy Rust może potrzebować informacji z HA:
```
```
```
temperatura z czujnika
obecność
otwarte okno
temperatura zewnętrzna
tryb domu
```
Nie robiłbym jednak bezpośredniego dostępu Rust → HA API jako podstawowej architektury.
Lepiej:
```
```
```
HA
│ WebSocket/API
GREE Integration
Rust Controller
```
Integracja może opcjonalnie przekazywać do controllera wybrane encje HA.
Na przykład:
```
```
```
External sensors
Outdoor temperature
→ sensor.outdoor_temperature
Living room temperature
→ sensor.living_room_temperature
Presence
→ binary_sensor.home_occupied
```
Controller może wtedy używać ich w automatyzacjach.
---
# Strefy ogrzewania
Wtedy możemy zrobić coś znacznie bardziej zaawansowanego.
```
```
```
Heating
├── Zone: Salon
│ ├── GREE Salon
│ ├── temperature sensor
│ └── schedule
├── Zone: Sypialnia
│ ├── GREE Sypialnia
│ └── temperature sensor
└── Zone: Biuro
├── GREE Biuro
└── temperature sensor
```
I każda strefa ma:
```
```
```
Comfort: 21.5°C
Night: 19°C
Away: 17°C
Frost protect: 10°C
Schedule:
06:00 → Comfort
08:30 → Away
16:00 → Comfort
23:00 → Night
```
---
# Technologia
Ja bym poszedł w:
```
```
```
Rust
├── tokio
├── axum
├── serde
├── serde_json
├── sqlx
├── SQLite
├── tracing
├── tower
└── tokio-tungstenite / axum WebSocket
```
Frontend:
```
```
```
React / TypeScript
```
albo, jeśli chcemy maksymalnie ograniczyć zasoby:
```
```
```
Leptos
```
Ale tutaj **nie komplikowałbym pierwszej wersji**. Rust backend + lekki frontend będzie rozsądniejszy.
---
# SQLite
Na początek:
```
```
```
gree.db
```
z tabelami mniej więcej:
```
```
```
devices
device_capabilities
device_state
device_history
zones
zone_devices
zone_sensors
automations
automation_conditions
automation_actions
schedules
settings
```
Bez PostgreSQL.
To będzie jedna usługa na jednym LXC, więc SQLite jest idealny.
---
# I jeszcze jedna decyzja architektoniczna
Nie robiłbym:
```
```
```
Rust
└── web server
└── GREE protocol
```
jako jednego wielkiego modułu.
Tylko:
```
```
```
src/
├── main.rs
├── api/
│ ├── rest.rs
│ ├── websocket.rs
│ └── auth.rs
├── gree/
│ ├── protocol.rs
│ ├── crypto.rs
│ ├── discovery.rs
│ ├── device.rs
│ ├── commands.rs
│ └── state.rs
├── devices/
│ ├── manager.rs
│ ├── capabilities.rs
│ └── registry.rs
├── automation/
│ ├── engine.rs
│ ├── conditions.rs
│ └── actions.rs
├── heating/
│ ├── controller.rs
│ ├── zones.rs
│ ├── hysteresis.rs
│ └── prediction.rs
├── scheduler/
├── database/
└── events/
```
To pozwoli nam później rozwijać projekt bez robienia spaghetti.
---
## Moim zdaniem docelowo powinno to wyglądać tak
```
```
```
┌────────────────────────────┐
│ HOME ASSISTANT │
│ │
│ Native HA entities │
│ │
│ GREE custom integration │
└─────────────┬──────────────┘
REST + WebSocket
┌─────────────▼──────────────┐
│ GREE CONTROLLER │
│ │
│ RUST │
│ │
│ ┌────────────────────────┐ │
│ │ GREE protocol │ │
│ ├────────────────────────┤ │
│ │ Device manager │ │
│ ├────────────────────────┤ │
│ │ State engine │ │
│ ├────────────────────────┤ │
│ │ Automation engine │ │
│ ├────────────────────────┤ │
│ │ Heating engine │ │
│ ├────────────────────────┤ │
│ │ Scheduler │ │
│ ├────────────────────────┤ │
│ │ SQLite │ │
│ └────────────────────────┘ │
│ │
│ Web UI │
└─────────────┬──────────────┘
UDP / 7000
┌─────────────┼──────────────┐
▼ ▼ ▼
GREE GREE GREE
```
**To jest architektura, którą bym przyjął.** Kontener/LXC jest niezależnym, autonomicznym serwisem, a HA dostaje normalne, natywne encje przez custom integration. Dzięki temu nawet całkowite wyłączenie HA nie zabija automatyki ogrzewania.
Następny krok zrobiłbym już praktycznie: **ustalić dokładnie protokół GREE/EWPE dla obecnych dongli, wybrać Rust crate/implementację jako punkt startowy i zaprojektować v1 API + model encji HA**, zanim zaczniemy pisać właściwy kod.
I jako dodatek do odczytu temperatur i lepsze odczyty jako opcja dodatkowa odczyt z api homeassistant zewnętrznego czujnika temperatury w pokoju z api homeassistant ale nie jako główne zrodlo informacji tylko jako opcja dodatkowa żeby wspomóc algorytm, zrób takamspecyfikacje uniezależnij to Dodatkowo interfejs musi być napełni kompatybilny z komórek bo to będzie główne miejsce sterowania, zastanawiam się na tailwind css z trybem ciemnym / jasnym Zrób takamspecyfikaje z tym co proponowałeś
Tak. W tej wersji zrobiłbym z tego **całkowicie niezależny kontroler GREE**, a Home Assistant byłby tylko jednym z możliwych źródeł danych i odbiorcą encji. Szczególnie ważne jest rozdzielenie **temperatury zmierzonej przez klimatyzator**, **temperatury z zewnętrznego sensora HA** oraz **temperatury używanej przez algorytm sterowania**.
Poniżej specyfikacja, którą można potraktować jako bazę projektu.
# Specyfikacja GREE Controller
## 1. Cel projektu
Samodzielna aplikacja działająca w osobnym środowisku:
- Rust
- LXC / kontener
- własny backend
- własna baza danych
- własny interfejs WWW
- komunikacja lokalna z klimatyzatorami GREE
- REST API
- WebSocket
- opcjonalna integracja z Home Assistant
- natywne encje Home Assistant
- własny silnik automatyzacji
- własny silnik sterowania ogrzewaniem/chłodzeniem
- obsługa zewnętrznych czujników temperatury
**Home Assistant nie jest wymagany do działania podstawowych funkcji systemu.**
---
# 2. Zasada nadrzędna
Architektura ma być **HA-independent**.
```
```
```
┌──────────────────────┐
│ GREE Controller │
│ │
│ Rust │
│ │
│ GREE protocol │
│ State engine │
│ Heating engine │
│ Automation engine │
│ Scheduler │
│ Web UI │
│ Database │
└──────────┬───────────┘
┌─────────────┴──────────────┐
│ │
GREE AC Home Assistant
│ │
│ Custom Integration
│ │
│ Entities / sensors
```
HA jest **integracją z kontrolerem**, a nie jego fundamentem.
---
# 3. Źródła temperatury
To będzie jeden z najważniejszych elementów projektu.
Każda strefa może mieć kilka źródeł temperatury.
### Źródło 1 — temperatura GREE
Podstawowe:
```
```
```
GREE internal temperature
```
np.
```
```
```
21.7°C
```
To jest temperatura raportowana przez klimatyzator.
---
### Źródło 2 — zewnętrzny sensor
Opcjonalnie:
```
```
```
Home Assistant
REST API / WebSocket
GREE Controller
external temperature
```
Przykład:
```
```
```
sensor.salon_temperature
```
Controller może cyklicznie pobierać:
```
```
```
21.2°C
```
ale **nie traktuje tego automatycznie jako zamiennika temperatury GREE**.
---
### Źródło 3 — temperatura obliczona
Najważniejsze:
```
```
```
CONTROL TEMPERATURE
```
czyli temperatura, którą faktycznie wykorzystuje algorytm.
Przykładowo:
```
```
```
GREE:
21.7°C
External:
21.2°C
Control:
21.4°C
```
Algorytm może korzystać z:
```
```
```
control_temperature =
weighted_average(
gree_temperature,
external_temperature
)
```
albo:
```
```
```
control_temperature =
external_temperature
```
jeżeli użytkownik jawnie tak skonfiguruje.
---
# 4. Konfiguracja źródła temperatury
W UI:
```
```
```
Temperature source
○ GREE internal sensor
○ External Home Assistant sensor
● Combined
GREE weight: 40%
External weight: 60%
External sensor:
sensor.salon_temperature
```
Dodatkowo:
```
```
```
Maximum allowed difference:
3.0°C
```
Jeżeli:
```
```
```
GREE = 21°C
External = 26°C
```
system nie powinien bezwarunkowo przyjąć 26°C.
Generuje:
```
```
```
Temperature sensor discrepancy
```
i może automatycznie przejść na GREE.
---
# 5. Confidence / jakość pomiaru
Każdy pomiar powinien mieć metadane:
```
```
```
temperature
timestamp
source
age
quality
```
Przykład:
```
```
```
External sensor
Temperature: 21.3°C
Age: 4 sec
Quality: 100%
```
Jeżeli HA przestanie odpowiadać:
```
```
```
Age: 83 sec
Quality: 20%
```
Po przekroczeniu timeoutu:
```
```
```
External sensor:
UNAVAILABLE
```
i system automatycznie korzysta z GREE.
To jest bardzo ważne dla niezależności.
---
# 6. Algorytm sterowania
Nie robimy prostego:
```
```
```
if temp < target:
heat()
```
tylko warstwę kontrolną.
```
```
```
Sensors
Sensor validation
Temperature estimator
Target temperature
Heating/Cooling controller
Safety limits
GREE command
```
---
# 7. Target temperature
Temperatura docelowa może wynikać z:
```
```
```
Manual
Schedule
Night
Away
Presence
External temperature
Automation
Heating zone
```
Przykład:
```
```
```
23:00
Night profile
19°C
06:00
Morning
21°C
08:30
Away
17°C
16:00
Home
21°C
```
---
# 8. Histereza
Konfigurowalna:
```
```
```
Target: 21°C
Hysteresis: 0.4°C
```
czyli:
```
```
```
< 20.6°C → HEAT
20.621.4°C → maintain
> 21.4°C → STOP
```
Dodatkowo:
```
```
```
Minimum ON time
Minimum OFF time
Minimum command interval
Maximum commands/hour
```
---
# 9. Inteligentne ogrzewanie
System powinien opcjonalnie analizować:
```
```
```
temperature history
target history
heating state
outside temperature
time
heating rate
```
i obliczać:
```
```
```
temperature change/hour
```
Przykład:
```
```
```
19:00 19.4°C
19:15 19.8°C
19:30 20.2°C
19:45 20.6°C
```
System:
```
```
```
Heating rate ≈ 1.6°C/hour
```
Może więc przewidzieć:
```
```
```
Target 21°C
ETA ≈ 15 min
```
W kolejnej wersji można dodać predykcję rozpoczęcia ogrzewania.
---
# 10. Strefy
Klimatyzator nie musi być bezpośrednio jednostką sterowania.
Tworzymy:
```
```
```
Zone
```
np.
```
```
```
Salon
```
która posiada:
```
```
```
Devices:
GREE Salon
Temperature sources:
GREE
sensor.salon_temperature
Target:
21°C
Schedule:
Salon schedule
Control:
Intelligent heating
```
Dzięki temu później można mieć:
```
```
```
Salon
├── GREE
├── Xiaomi temperature sensor
└── window sensor
Sypialnia
├── GREE
└── temperature sensor
```
---
# 11. Home Assistant jako opcjonalne źródło danych
Controller posiada moduł:
```
```
```
home_assistant/
```
który jest **opcjonalny**.
Konfiguracja:
```
```
```
Home Assistant integration
Enabled: YES
URL:
http://homeassistant:8123
Token:
************
Poll interval:
10 sec
```
Możemy pobierać:
```
```
```
sensor.*
binary_sensor.*
input_boolean.*
input_number.*
```
ale użytkownik wskazuje konkretne encje.
Np.:
```
```
```
External temperature:
HA entity:
sensor.salon_temperature
```
Nie robimy automatycznego pobierania całego HA.
---
# 12. HA nie może być wymaganiem
Jeżeli:
```
```
```
HA DOWN
```
system nadal:
- steruje GREE
- wykonuje harmonogram
- wykonuje automatyzacje lokalne
- używa temperatury GREE
- działa przez Web UI
- przechowuje historię
Jedynie:
```
```
```
External HA sensors
```
stają się niedostępne.
---
# 13. Integracja Home Assistant
Po stronie HA:
```
```
```
GREE Controller
Custom Integration
Device
Entities
```
Dla klimatyzatora:
```
```
```
climate.gree_salon
```
oraz np.:
```
```
```
sensor.gree_salon_temperature
sensor.gree_salon_target_temperature
sensor.gree_salon_power
sensor.gree_salon_connection
sensor.gree_salon_fan
```
---
# 14. Encja Climate
Powinna być maksymalnie kompatybilna z natywnym HA.
Obsługa:
```
```
```
OFF
HEAT
COOL
AUTO
DRY
FAN_ONLY
```
oraz:
```
```
```
target temperature
fan mode
preset mode
swing
```
---
# 15. Dodatkowe encje
W zależności od capabilities urządzenia:
```
```
```
switch.gree_salon_turbo
switch.gree_salon_quiet
switch.gree_salon_sleep
switch.gree_salon_light
switch.gree_salon_xfan
```
oraz:
```
```
```
sensor.gree_salon_temperature
sensor.gree_salon_humidity
sensor.gree_salon_power
sensor.gree_salon_signal
```
Nie tworzymy encji dla funkcji, których konkretna jednostka nie obsługuje.
---
# 16. Web UI — bardzo ważne
Ponieważ telefon będzie głównym sposobem obsługi, projektujemy UI **mobile-first**, a nie desktop-first.
Technologia:
**Tailwind CSS**
z:
```
```
```
Dark mode
Light mode
System mode
```
czyli:
```
```
```
Appearance
● System
○ Dark
○ Light
```
---
# 17. Mobile UI
Na telefonie dashboard:
```
```
```
┌────────────────────────┐
│ GREE Controller ☰ │
├────────────────────────┤
│ │
│ SALON │
│ │
│ 21.4°C │
│ │
│ Target 21°C │
│ │
│ 🔥 HEATING │
│ │
21°C + │
│ │
│ HEAT AUTO │
│ │
│ ────────────────────── │
│ │
│ Fan Auto │
│ Swing Auto │
│ │
│ Turbo ○ │
│ Quiet ● │
│ Sleep ○ │
│ │
└────────────────────────┘
```
Bez mikroskopijnych przycisków.
Minimalny rozmiar interaktywnych elementów powinien być około:
```
```
```
44 × 44 px
```
---
# 18. Mobile navigation
Dolny pasek:
```
```
```
┌────────────────────────┐
│ │
│ CONTENT │
│ │
├────────────────────────┤
│ 🏠 🌡 ⚙ │
│ Home Zones More │
└────────────────────────┘
```
Na desktopie może przełączać się w sidebar.
---
# 19. Dashboard
Główne widoki:
```
```
```
Dashboard
Devices
Zones
Heating
Automations
Schedules
History
Diagnostics
Settings
```
---
# 20. Automatyzacje
GUI:
```
```
```
WHEN
Temperature
[ Salon ]
[ < ]
[ 20°C ]
AND
Time
[ 06:00 - 22:00 ]
THEN
Climate
[ Salon ]
Mode:
[ HEAT ]
Target:
[ 21°C ]
```
Ale silnik jest w Rust.
HA nie jest potrzebny.
---
# 21. WebSocket
Frontend nie powinien wykonywać ciągłego:
```
```
```
GET /state
GET /state
GET /state
```
Tylko:
```
```
```
Browser
│ WebSocket
Rust
Event stream
```
Przykładowo:
```
```
```
device_state_changed
temperature_changed
device_online
device_offline
automation_triggered
heating_state_changed
```
Dzięki temu UI będzie faktycznie „live”.
---
# 22. API
REST:
```
```
```
/api/v1/system
/api/v1/devices
/api/v1/devices/{id}
/api/v1/devices/{id}/state
/api/v1/devices/{id}/commands
/api/v1/zones
/api/v1/zones/{id}
/api/v1/automations
/api/v1/schedules
/api/v1/sensors
/api/v1/history
/api/v1/ha
```
WebSocket:
```
```
```
/api/v1/ws
```
---
# 23. Authentication
Ponieważ będzie to osobny host, od początku robimy:
```
```
```
API authentication
```
np.:
```
```
```
Bearer token
```
oraz osobne konto dla Web UI.
W przyszłości:
```
```
```
OIDC
```
ale nie jest potrzebne w v1.
---
# 24. GREE Protocol Layer
Cały protokół izolujemy:
```
```
```
gree/
├── discovery.rs
├── protocol.rs
├── crypto.rs
├── packet.rs
├── bind.rs
├── commands.rs
├── state.rs
└── capabilities.rs
```
Dzięki temu reszta programu **nie wie, że pod spodem jest GREE**.
To pozwoli kiedyś potencjalnie dodać:
```
```
```
gree/
haier/
midea/
```
bez przebudowy całego systemu.
---
# 25. Model urządzenia
```
```
```
Device
├── id
├── name
├── ip
├── mac
├── model
├── firmware
├── protocol
├── capabilities
├── state
├── last_seen
└── availability
```
---
# 26. Stan urządzenia
```
```
```
DeviceState
├── power
├── mode
├── target_temperature
├── room_temperature
├── humidity
├── fan_mode
├── swing_vertical
├── swing_horizontal
├── preset
├── turbo
├── quiet
└── ...
```
Nie zakładamy, że każde GREE ma wszystkie pola.
---
# 27. Capability discovery
Po wykryciu urządzenia controller określa:
```
```
```
supports_heat
supports_cool
supports_dry
supports_fan
supports_turbo
supports_quiet
supports_swing
supports_humidity
...
```
Frontend oraz HA integration korzystają z capabilities.
---
# 28. Baza
Na początek:
**SQLite**
```
```
```
devices
device_capabilities
device_state
temperature_history
events
zones
zone_devices
zone_sensors
automations
automation_conditions
automation_actions
schedules
ha_connections
ha_entities
settings
```
Bez zewnętrznego PostgreSQL.
---
# 29. Historia
Zapisywane:
```
```
```
temperature
target
mode
power
fan
heating state
source temperature
```
Dzięki temu UI może pokazać:
```
```
```
Temperature
22°C ┤ ╭──────
21°C ┤ ──────╯
20°C ┤
└────────────────
```
oraz:
```
```
```
GREE: 21.7°C
External: 21.2°C
Control: 21.4°C
```
To ostatnie jest szczególnie istotne diagnostycznie.
---
# 30. Diagnostics
Każdy GREE:
```
```
```
Status: ONLINE
IP:
192.168.x.x
Last seen:
1.2 sec ago
Latency:
12 ms
Packets:
12342
Errors:
0
Last error:
None
```
Możliwość:
```
```
```
[ Ping ]
[ Refresh state ]
[ Rediscover ]
[ Rebind ]
```
---
# 31. Kontener
Docelowo:
```
```
```
LXC
└── Debian
└── gree-controller
├── /usr/bin/gree-controller
├── /etc/gree-controller/
└── /var/lib/gree-controller/
└── gree.db
```
Service:
```
```
```
systemd
```
czyli:
```
```
```
gree-controller.service
```
Nie potrzebujemy Dockera.
---
# 32. Aktualizacja
Rust daje nam prosty model:
```
```
```
binary
+
database migrations
```
Możemy później zrobić:
```
```
```
v0.1
v0.2
v0.3
...
```
z migracjami SQLite.
---
# 33. Struktura projektu
Docelowo:
```
```
```
gree-controller/
├── Cargo.toml
├── crates/
│ ├── gree-protocol/
│ ├── gree-core/
│ ├── gree-api/
│ └── gree-ha/
├── backend/
│ └── src/
├── frontend/
│ ├── src/
│ ├── public/
│ └── ...
├── ha-integration/
│ └── custom_components/
│ └── gree_controller/
├── migrations/
├── packaging/
│ ├── systemd/
│ └── debian/
└── README.md
```
Przy większym projekcie podział na Cargo workspace będzie bardzo wygodny.
---
# 34. Najważniejsza zasada dotycząca HA
**Nie kopiujemy logiki między Rust i HA.**
Nie:
```
```
```
HA heating logic
+
Rust heating logic
```
Tylko:
```
```
```
Rust Controller
┌─────────────┴──────────────┐
│ │
heating engine automation engine
│ │
└─────────────┬──────────────┘
Controller API
Home Assistant
Entities
```
HA pokazuje i steruje.
**Rust podejmuje decyzje.**
---
# 35. Kolejność implementacji
Nie zaczynałbym od GUI.
### Faza 1 — GREE protocol
```
```
```
[ ] discovery
[ ] bind
[ ] encryption
[ ] status
[ ] commands
[ ] capabilities
[ ] reconnect
```
### Faza 2 — Core
```
```
```
[ ] Device Manager
[ ] State Manager
[ ] event system
[ ] SQLite
[ ] configuration
```
### Faza 3 — API
```
```
```
[ ] REST
[ ] WebSocket
[ ] authentication
```
### Faza 4 — Web UI
```
```
```
[ ] Tailwind
[ ] dark/light
[ ] mobile-first
[ ] dashboard
[ ] devices
[ ] zones
[ ] history
[ ] diagnostics
```
### Faza 5 — Heating Engine
```
```
```
[ ] target
[ ] schedules
[ ] hysteresis
[ ] minimum run time
[ ] sensor weighting
[ ] sensor fallback
[ ] heating zones
[ ] prediction
```
### Faza 6 — Automations
```
```
```
[ ] triggers
[ ] conditions
[ ] actions
[ ] scheduler
[ ] GUI builder
```
### Faza 7 — Home Assistant
```
```
```
[ ] config flow
[ ] device discovery
[ ] climate
[ ] sensors
[ ] switches
[ ] diagnostics
[ ] external HA sensors
```
---
## Jedna rzecz, którą zmieniłbym względem pierwotnego pomysłu
Nie nazywałbym tego nawet wewnętrznie „integracją GREE do Home Assistant”.
To powinien być **samodzielny GREE Controller**, a Home Assistant będzie jednym z klientów.
Wtedy mamy:
```
```
```
GREE Controller
/ | \
/ | \
▼ ▼ ▼
Web UI HA REST API
HA entities
```
I dokładnie spełniamy Twój warunek:
**osobne środowisko jest obowiązkowe**, HA nie jest wymagane do działania, zewnętrzny czujnik HA jest tylko **opcjonalnym źródłem wspomagającym algorytm**, a użytkownik może praktycznie całe sterowanie wykonywać z telefonu przez responsywny UI.
**Tailwind + dark/light + mobile-first** jest tutaj bardzo dobrym wyborem. Zrobiłbym nawet projekt UI od początku z założeniem „telefon jako primary, desktop jako secondary”, zamiast później próbować dopasowywać desktopowy panel do telefonu.