# Air conditioner
Ogólne sterowanie klimatyzatorem. Zawiera logikę wspólną dla wszystkich urządzeń, a komunikację deleguje do dołączonego bloku implementacji, który jako jedyny zna daną markę lub model.
# Bloki implementujące
| Nazwa | ID | Opis |
|---|---|---|
| Daikin BRP069 | daikin_brp069 | Steruje klimatyzatorem Daikin w sieci lokalnej, korzystając z nieszyfrowanego API HTTP adapterów Wi-Fi z rodziny BRP069 (BRP069A4x, BRP069B4x, BRP072A4x). Nowsze adaptery BRP072C4x i BRP084 używają innych protokołów i wymagają własnych bloków. |
Air conditioner
P
M
TT
FS
SV
SH
CLN
P
M
TT
FS
SV
SH
CLN
RT
F
# Wejścia
| ID | Skrót | Nazwa | Typ | Domyślnie | Opis |
|---|---|---|---|---|---|
power | P | Zasilanie | BOOLEAN | false | Włącza urządzenie przy stanie wysokim, wyłącza przy niskim |
mode | M | Tryb | NUMBER | 0 | Tryb pracy: 0 - auto, 1 - grzanie, 2 - chłodzenie, 3 - osuszanie, 4 - tylko wentylator. Urządzenie bez danego trybu użyje najbliższego odpowiednika. |
target_temp | TT | Temperatura docelowa | NUMBER | 21 | Żądana temperatura docelowa, ograniczona do skonfigurowanego zakresu |
fan_speed | FS | Prędkość wentylatora | NUMBER | 0 | Prędkość wentylatora: 0 - auto, 1 - cicho, 2 - niska, 3 - średnia, 4 - wysoka, 5 - maksymalna (boost). Urządzenie z mniejszą liczbą stopni użyje najbliższego odpowiednika. |
swing_v | SV | Nawiew pionowy | NUMBER | 0 | Żaluzja pionowa: 0 - auto, 1 - oscylacja, 2 do 6 - stała pozycja od najwyższej do najniższej |
swing_h | SH | Nawiew poziomy | NUMBER | 0 | Żaluzja pozioma: 0 - auto, 1 - oscylacja, 2 do 6 - stała pozycja od skrajnie lewej do skrajnie prawej |
air_clean | CLN | Oczyszczanie powietrza | BOOLEAN | false | Włącza funkcję oczyszczania powietrza w urządzeniu (jonizator, streamer, filtr plazmowy). Ignorowane przez urządzenia bez tej funkcji. |
# Wyjścia
| ID | Skrót | Nazwa | Typ | Domyślnie | Opis |
|---|---|---|---|---|---|
power | P | Zasilanie | BOOLEAN | false | Stan wysoki, gdy urządzenie jest włączone. Odczytany z urządzenia, gdy implementacja udostępnia informację zwrotną, w przeciwnym razie stan zadany. |
mode | M | Tryb | NUMBER | 0 | Aktualny tryb pracy, w tym samym kodowaniu co wejście trybu. Urządzenia raportujące swój stan rozwijają tutaj tryb auto na grzanie lub chłodzenie. |
target_temp | TT | Temperatura docelowa | NUMBER | 0 | Temperatura docelowa, na której pracuje urządzenie, po zastosowaniu skonfigurowanego zakresu |
fan_speed | FS | Prędkość wentylatora | NUMBER | 0 | Zadana prędkość wentylatora, w tym samym kodowaniu co wejście prędkości wentylatora. Żadne urządzenie jej nie raportuje, więc jest to zawsze wartość zadana. |
swing_v | SV | Nawiew pionowy | NUMBER | 0 | Zadane ustawienie kierownicy pionowej, w tym samym kodowaniu co wejście nawiewu pionowego. Żadne urządzenie go nie raportuje, więc jest to zawsze wartość zadana. |
swing_h | SH | Nawiew poziomy | NUMBER | 0 | Zadane ustawienie kierownicy poziomej, w tym samym kodowaniu co wejście nawiewu poziomego. Żadne urządzenie go nie raportuje, więc jest to zawsze wartość zadana. |
air_clean | CLN | Oczyszczanie powietrza | BOOLEAN | false | Zadane ustawienie oczyszczania powietrza. Żadne urządzenie go nie raportuje, więc jest to zawsze wartość zadana. |
room_temp | RT | Temperatura pomieszczenia | NUMBER | -1000 | Temperatura wewnętrzna zmierzona przez urządzenie. -1000, gdy urządzenie jej nie raportuje. |
fault | F | Awaria | NUMBER | 0 | 0 - brak awarii, -1 - brak komunikacji z urządzeniem, powyżej 0 - kod błędu urządzenia |
# Konfiguracja
| ID | Nazwa | Typ | Domyślnie | Jednostka | Opis |
|---|---|---|---|---|---|
min_target_temp | Minimalna temperatura docelowa | NUMBER | 10 | °C | Dolny limit wejścia temperatury docelowej. Urządzenie może go dodatkowo zawęzić. Szczegóły: ≤ max_target_temp |
max_target_temp | Maksymalna temperatura docelowa | NUMBER | 30 | °C | Górny limit wejścia temperatury docelowej. Urządzenie może go dodatkowo zawęzić. Szczegóły: ≥ min_target_temp |
# Stan
| ID | Nazwa | Typ | Domyślnie | Jednostka | Opis |
|---|---|---|---|---|---|
unit_power | Raportowane zasilanie | NUMBER | -1 | Stan zasilania raportowany przez blok implementacji: -1 nieznany, 0 wyłączone, 1 włączone | |
unit_mode | Raportowany tryb | NUMBER | -1 | Tryb raportowany przez blok implementacji, -1 gdy nieznany | |
unit_target_temp | Raportowana temperatura docelowa | NUMBER | -1000.0 | °C | Temperatura docelowa raportowana przez blok implementacji, -1000 gdy nieznana |
unit_room_temp | Raportowana temperatura pomieszczenia | NUMBER | -1000.0 | °C | Temperatura wewnętrzna raportowana przez blok implementacji, -1000 gdy nieznana |
unit_fault | Raportowana awaria | NUMBER | 0 | Awaria raportowana przez blok implementacji | |
invoke_rc | Wynik ostatniego wywołania implementacji | NUMBER | 0 | Wartość zwrócona przez ostatnie wywołanie implementacji, publikowana jako awaria, gdy urządzenie nie raportuje własnej | |
sent | Stan wysłany | BOOLEAN | false | Ustawione, gdy stan został przyjęty przez urządzenie co najmniej raz | |
sent_power | Wysłane zasilanie | BOOLEAN | false | Ostatni stan zasilania przyjęty przez urządzenie | |
sent_mode | Wysłany tryb | NUMBER | 0 | Ostatni tryb przyjęty przez urządzenie | |
sent_target_temp | Wysłana temperatura docelowa | NUMBER | 0.0 | °C | Ostatnia temperatura docelowa przyjęta przez urządzenie |
sent_fan_speed | Wysłana prędkość wentylatora | NUMBER | 0 | Ostatnia prędkość wentylatora przyjęta przez urządzenie | |
sent_swing_v | Wysłany nawiew pionowy | NUMBER | 0 | Ostatnie ustawienie żaluzji pionowej przyjęte przez urządzenie | |
sent_swing_h | Wysłany nawiew poziomy | NUMBER | 0 | Ostatnie ustawienie żaluzji poziomej przyjęte przez urządzenie | |
sent_air_clean | Wysłane oczyszczanie powietrza | BOOLEAN | false | Ostatnie ustawienie oczyszczania powietrza przyjęte przez urządzenie |
# Kod źródłowy
Pokaż kod Volang
// Air conditioner - abstract block.
//
// Turns the input channels into one desired unit state and hands it to the
// attached implementation (driver), the only part that knows how to reach a
// particular unit (IR, LAN, Modbus, ...).
//
// Value encodings, shared by the inputs, the outputs and the driver call:
// mode 0 auto, 1 heating, 2 cooling, 3 drying, 4 fan only
// fan_speed 0 auto, 1 quiet, 2 low, 3 medium, 4 high, 5 powerful
// swing_v 0 auto, 1 swing, 2..6 fixed vane (2 highest .. 6 lowest)
// swing_h 0 auto, 1 swing, 2..6 fixed vane (2 leftmost .. 6 rightmost)
// fault 0 none, -1 unit unreachable, > 0 unit specific error code
// A unit that cannot do what was asked for falls back to its closest
// equivalent - that mapping belongs to the driver.
//
// The driver must declare one function:
//
// ac_apply(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)
// Pushes the complete desired state, called whenever a value changes.
// It only has to accept the work, not finish it: a driver on the network
// returns as soon as the request is out, and one still busy with an
// earlier exchange keeps the state and sends it once that is over.
// Returns 0 when accepted, otherwise an error code that is published on
// the fault output.
//
// Whether and how often the unit is polled for its status is up to the driver,
// which can be scheduled on its own. Either way it reports back through this
// block's state, then returns non-zero from the run that stored the readings,
// so the runtime calls std::on_driver_update here:
// unit_power -1 unknown, 0 off, 1 on
// unit_mode -1 unknown, otherwise a mode value
// unit_target_temp -1000 unknown, otherwise °C
// unit_room_temp -1000 unknown, otherwise °C
// unit_fault the fault value to publish
// Whatever is left unknown is published as the value this block commanded.
fn choice(value, hi) {
v = math::int(math::round(value))
if (v < 0) {
return 0
}
if (v > hi) {
return hi
}
return v
}
fn clamp(value, lo, hi) {
if (value < lo) {
return lo
}
if (value > hi) {
return hi
}
return value
}
// Airflow and air cleaning have no counterpart in the readings - no unit
// reports them back - so they are published exactly as commanded.
fn publish(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean) {
unit_power = state::get("unit_power")
if (unit_power >= 0) {
power = unit_power != 0
}
unit_mode = state::get("unit_mode")
if (unit_mode >= 0) {
mode = unit_mode
}
unit_target_temp = state::get("unit_target_temp")
if (unit_target_temp > -1000) {
target_temp = unit_target_temp
}
fault = state::get("unit_fault")
if (fault == 0) {
fault = state::get("invoke_rc")
}
output::set("power", power)
output::set("mode", mode)
output::set("target_temp", target_temp)
output::set("fan_speed", fan_speed)
output::set("swing_v", swing_v)
output::set("swing_h", swing_h)
output::set("air_clean", air_clean)
output::set("room_temp", state::get("unit_room_temp"))
output::set("fault", fault)
}
fn changed(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean) {
if (!state::get("sent")) {
return true
}
if (state::get("sent_power") != power) {
return true
}
if (state::get("sent_air_clean") != air_clean) {
return true
}
if (state::get("sent_mode") != mode) {
return true
}
if (state::get("sent_target_temp") != target_temp) {
return true
}
if (state::get("sent_fan_speed") != fan_speed) {
return true
}
if (state::get("sent_swing_v") != swing_v) {
return true
}
if (state::get("sent_swing_h") != swing_h) {
return true
}
return false
}
// The last readings describe the unit before a command, so they must not mask
// what was just asked for; the read that follows fills them in again. The room
// temperature is a measurement, not a setting, and stays.
fn forget_readings() {
state::set("unit_power", -1)
state::set("unit_mode", -1)
state::set("unit_target_temp", -1000.0)
}
fn remember(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean) {
state::set("sent_power", power)
state::set("sent_mode", mode)
state::set("sent_target_temp", target_temp)
state::set("sent_fan_speed", fan_speed)
state::set("sent_swing_v", swing_v)
state::set("sent_swing_h", swing_h)
state::set("sent_air_clean", air_clean)
state::set("sent", true)
}
// Called by the runtime after the driver stored fresh readings. The commanded
// values are only a fallback for whatever the unit did not report.
extern fn std::on_driver_update() {
power = state::get("sent_power")
mode = state::get("sent_mode")
target_temp = state::get("sent_target_temp")
fan_speed = state::get("sent_fan_speed")
swing_v = state::get("sent_swing_v")
swing_h = state::get("sent_swing_h")
air_clean = state::get("sent_air_clean")
publish(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)
}
power = input::get("power")
mode = choice(input::get("mode"), 4)
fan_speed = choice(input::get("fan_speed"), 5)
swing_v = choice(input::get("swing_v"), 6)
swing_h = choice(input::get("swing_h"), 6)
air_clean = input::get("air_clean")
min_temp = config::get("min_target_temp")
max_temp = config::get("max_target_temp")
target_temp = clamp(input::get("target_temp"), min_temp, max_temp)
if (changed(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)) {
// Report the commanded state right away, so the outputs stay meaningful
// even when the driver call below fails or no driver is attached at all.
publish(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)
rc = std::invoke("ac_apply", power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)
state::set("invoke_rc", rc)
if (rc == 0) {
remember(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)
forget_readings()
}
}
publish(power, mode, target_temp, fan_speed, swing_v, swing_h, air_clean)