3085 lines
47 KiB
Markdown
3085 lines
47 KiB
Markdown
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.6–21.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. |