diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/ha-configs/151/dashboards/ibsystem_dashboard.yaml b/ha-configs/151/dashboards/ibsystem_dashboard.yaml new file mode 100644 index 0000000..ca1aba6 --- /dev/null +++ b/ha-configs/151/dashboards/ibsystem_dashboard.yaml @@ -0,0 +1,246 @@ +title: IBSystem Sterowniki +views: + - title: Wszystkie sterowniki + path: all + icon: mdi:lightbulb-group + cards: + - type: horizontal-stack + cards: + - type: button + name: "WSZYSTKIE ON" + icon: mdi:lightbulb-group + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_on + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + - type: button + name: "WSZYSTKIE OFF" + icon: mdi:lightbulb-group-off + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_off + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + + - type: entities + title: "ID30" + entities: + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + name: "DO.3" + + - type: entities + title: "ID31" + entities: + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + name: "DO.3" + + - type: entities + title: "ID32" + entities: + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + name: "DO.3" + + - type: entities + title: "ID33" + entities: + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + name: "DO.3" + + - type: entities + title: "ID34" + entities: + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + name: "DO.3" + + - type: entities + title: "ID35" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "ID36" + entities: + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + name: "DO.3" + + - type: entities + title: "ID37" + entities: + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + name: "DO.3" + + - type: entities + title: "ID38" + entities: + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + name: "DO.3" + + - type: entities + title: "ID39" + entities: + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + name: "DO.3" + + - type: entities + title: "ID40" + entities: + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + name: "DO.3" diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/ha-configs/151/dashboards/ibsystem_dashboard.yaml b/ha-configs/151/dashboards/ibsystem_dashboard.yaml new file mode 100644 index 0000000..ca1aba6 --- /dev/null +++ b/ha-configs/151/dashboards/ibsystem_dashboard.yaml @@ -0,0 +1,246 @@ +title: IBSystem Sterowniki +views: + - title: Wszystkie sterowniki + path: all + icon: mdi:lightbulb-group + cards: + - type: horizontal-stack + cards: + - type: button + name: "WSZYSTKIE ON" + icon: mdi:lightbulb-group + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_on + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + - type: button + name: "WSZYSTKIE OFF" + icon: mdi:lightbulb-group-off + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_off + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + + - type: entities + title: "ID30" + entities: + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + name: "DO.3" + + - type: entities + title: "ID31" + entities: + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + name: "DO.3" + + - type: entities + title: "ID32" + entities: + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + name: "DO.3" + + - type: entities + title: "ID33" + entities: + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + name: "DO.3" + + - type: entities + title: "ID34" + entities: + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + name: "DO.3" + + - type: entities + title: "ID35" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "ID36" + entities: + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + name: "DO.3" + + - type: entities + title: "ID37" + entities: + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + name: "DO.3" + + - type: entities + title: "ID38" + entities: + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + name: "DO.3" + + - type: entities + title: "ID39" + entities: + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + name: "DO.3" + + - type: entities + title: "ID40" + entities: + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + name: "DO.3" diff --git a/ha-configs/151/dashboards/id35_dashboard.yaml b/ha-configs/151/dashboards/id35_dashboard.yaml new file mode 100644 index 0000000..404f9cd --- /dev/null +++ b/ha-configs/151/dashboards/id35_dashboard.yaml @@ -0,0 +1,33 @@ +title: IBSystem ID35 +views: + - title: Sterownik ID35 + path: id35 + icon: mdi:lightbulb-group + cards: + - type: markdown + content: | + ## Sterowanie oświetleniem ID35 + + - type: entities + title: "Wyjścia cyfrowe (MQTT)" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "Wejścia cyfrowe" + entities: + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_a + name: "DI.0.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_b + name: "DI.0.B" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_a + name: "DI.1.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_b + name: "DI.1.B" diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/ha-configs/151/dashboards/ibsystem_dashboard.yaml b/ha-configs/151/dashboards/ibsystem_dashboard.yaml new file mode 100644 index 0000000..ca1aba6 --- /dev/null +++ b/ha-configs/151/dashboards/ibsystem_dashboard.yaml @@ -0,0 +1,246 @@ +title: IBSystem Sterowniki +views: + - title: Wszystkie sterowniki + path: all + icon: mdi:lightbulb-group + cards: + - type: horizontal-stack + cards: + - type: button + name: "WSZYSTKIE ON" + icon: mdi:lightbulb-group + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_on + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + - type: button + name: "WSZYSTKIE OFF" + icon: mdi:lightbulb-group-off + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_off + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + + - type: entities + title: "ID30" + entities: + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + name: "DO.3" + + - type: entities + title: "ID31" + entities: + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + name: "DO.3" + + - type: entities + title: "ID32" + entities: + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + name: "DO.3" + + - type: entities + title: "ID33" + entities: + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + name: "DO.3" + + - type: entities + title: "ID34" + entities: + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + name: "DO.3" + + - type: entities + title: "ID35" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "ID36" + entities: + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + name: "DO.3" + + - type: entities + title: "ID37" + entities: + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + name: "DO.3" + + - type: entities + title: "ID38" + entities: + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + name: "DO.3" + + - type: entities + title: "ID39" + entities: + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + name: "DO.3" + + - type: entities + title: "ID40" + entities: + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + name: "DO.3" diff --git a/ha-configs/151/dashboards/id35_dashboard.yaml b/ha-configs/151/dashboards/id35_dashboard.yaml new file mode 100644 index 0000000..404f9cd --- /dev/null +++ b/ha-configs/151/dashboards/id35_dashboard.yaml @@ -0,0 +1,33 @@ +title: IBSystem ID35 +views: + - title: Sterownik ID35 + path: id35 + icon: mdi:lightbulb-group + cards: + - type: markdown + content: | + ## Sterowanie oświetleniem ID35 + + - type: entities + title: "Wyjścia cyfrowe (MQTT)" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "Wejścia cyfrowe" + entities: + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_a + name: "DI.0.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_b + name: "DI.0.B" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_a + name: "DI.1.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_b + name: "DI.1.B" diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml new file mode 100644 index 0000000..48a6859 --- /dev/null +++ b/ibsystem/config.yaml @@ -0,0 +1,30 @@ +mqtt: + brokers: + - name: "ha" + host: "192.168.50.151" + port: 1883 + user: "mqtt" + password: "mqtt123" + prefix: "ibsystem" + ha_prefix: "homeassistant" + +ibsystem: + host: "127.0.0.1" + port: 2001 + rs: 0 + max_ids: 40 + full_ids: [1] + timeout_ms: 10000 + +polling: + interval_sec: 2.0 + parallel_workers: 8 + +logging: + level: "DEBUG" + file: "/tmp/ibsystem2mqtt.log" + +http: + enabled: true + host: "0.0.0.0" + port: 8080 diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/ha-configs/151/dashboards/ibsystem_dashboard.yaml b/ha-configs/151/dashboards/ibsystem_dashboard.yaml new file mode 100644 index 0000000..ca1aba6 --- /dev/null +++ b/ha-configs/151/dashboards/ibsystem_dashboard.yaml @@ -0,0 +1,246 @@ +title: IBSystem Sterowniki +views: + - title: Wszystkie sterowniki + path: all + icon: mdi:lightbulb-group + cards: + - type: horizontal-stack + cards: + - type: button + name: "WSZYSTKIE ON" + icon: mdi:lightbulb-group + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_on + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + - type: button + name: "WSZYSTKIE OFF" + icon: mdi:lightbulb-group-off + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_off + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + + - type: entities + title: "ID30" + entities: + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + name: "DO.3" + + - type: entities + title: "ID31" + entities: + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + name: "DO.3" + + - type: entities + title: "ID32" + entities: + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + name: "DO.3" + + - type: entities + title: "ID33" + entities: + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + name: "DO.3" + + - type: entities + title: "ID34" + entities: + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + name: "DO.3" + + - type: entities + title: "ID35" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "ID36" + entities: + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + name: "DO.3" + + - type: entities + title: "ID37" + entities: + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + name: "DO.3" + + - type: entities + title: "ID38" + entities: + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + name: "DO.3" + + - type: entities + title: "ID39" + entities: + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + name: "DO.3" + + - type: entities + title: "ID40" + entities: + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + name: "DO.3" diff --git a/ha-configs/151/dashboards/id35_dashboard.yaml b/ha-configs/151/dashboards/id35_dashboard.yaml new file mode 100644 index 0000000..404f9cd --- /dev/null +++ b/ha-configs/151/dashboards/id35_dashboard.yaml @@ -0,0 +1,33 @@ +title: IBSystem ID35 +views: + - title: Sterownik ID35 + path: id35 + icon: mdi:lightbulb-group + cards: + - type: markdown + content: | + ## Sterowanie oświetleniem ID35 + + - type: entities + title: "Wyjścia cyfrowe (MQTT)" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "Wejścia cyfrowe" + entities: + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_a + name: "DI.0.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_b + name: "DI.0.B" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_a + name: "DI.1.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_b + name: "DI.1.B" diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml new file mode 100644 index 0000000..48a6859 --- /dev/null +++ b/ibsystem/config.yaml @@ -0,0 +1,30 @@ +mqtt: + brokers: + - name: "ha" + host: "192.168.50.151" + port: 1883 + user: "mqtt" + password: "mqtt123" + prefix: "ibsystem" + ha_prefix: "homeassistant" + +ibsystem: + host: "127.0.0.1" + port: 2001 + rs: 0 + max_ids: 40 + full_ids: [1] + timeout_ms: 10000 + +polling: + interval_sec: 2.0 + parallel_workers: 8 + +logging: + level: "DEBUG" + file: "/tmp/ibsystem2mqtt.log" + +http: + enabled: true + host: "0.0.0.0" + port: 8080 diff --git a/ibsystem/ibsystem2mqtt_v5.py b/ibsystem/ibsystem2mqtt_v5.py new file mode 100755 index 0000000..5650a9a --- /dev/null +++ b/ibsystem/ibsystem2mqtt_v5.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +"""IBSystem -> MQTT Bridge for Home Assistant v5""" +import subprocess, re, time, json, threading, logging, signal, sys +from pathlib import Path +from concurrent.futures import ThreadPoolExecutor, as_completed +from dataclasses import dataclass +from http.server import HTTPServer, BaseHTTPRequestHandler +from datetime import datetime +import paho.mqtt.client as mqtt +try: + import yaml + HAS_YAML = True +except ImportError: + HAS_YAML = False + +DEFAULT_CONFIG = {"mqtt": {"brokers": [{"name": "ha", "host": "ha.local", "port": 1883}], "user": "mqtt", "password": "mqtt123", "prefix": "ibsystem", "ha_prefix": "homeassistant"}, "ibsystem": {"host": "127.0.0.1", "port": 2001, "rs": 0, "max_ids": 40, "full_ids": [1], "timeout_ms": 10000}, "polling": {"interval_sec": 2.0, "parallel_workers": 8}, "logging": {"level": "INFO", "file": None}, "http": {"enabled": True, "host": "0.0.0.0", "port": 8080}, "friendly_names": {}} + +class BridgeStats: + def __init__(self): + self.start_time = datetime.now() + self.poll_cycles = 0 + self.last_poll_time = None + self.last_poll_duration = 0.0 + self.devices_discovered = 0 + self.entities_discovered = 0 + self.messages_received = 0 + self.commands_executed = 0 + self.errors = 0 + self.last_error = None + self._lock = threading.Lock() + def record_poll(self, duration, devices): + with self._lock: self.poll_cycles += 1; self.last_poll_time = datetime.now(); self.last_poll_duration = duration; self.devices_discovered = devices + def record_entity(self): + with self._lock: self.entities_discovered += 1 + def record_command(self): + with self._lock: self.commands_executed += 1; self.messages_received += 1 + def record_error(self, error): + with self._lock: self.errors += 1; self.last_error = f"{datetime.now().isoformat()}: {error}" + def to_dict(self): + with self._lock: + uptime = datetime.now() - self.start_time + return {"status": "running", "uptime_seconds": int(uptime.total_seconds()), "uptime_human": str(uptime).split('.')[0], "start_time": self.start_time.isoformat(), "poll_cycles": self.poll_cycles, "last_poll_time": self.last_poll_time.isoformat() if self.last_poll_time else None, "last_poll_duration_ms": round(self.last_poll_duration * 1000, 1), "devices_discovered": self.devices_discovered, "entities_discovered": self.entities_discovered, "messages_received": self.messages_received, "commands_executed": self.commands_executed, "errors": self.errors, "last_error": self.last_error} +STATS = BridgeStats() + +def setup_logging(config): + level = getattr(logging, config.get("level", "INFO").upper(), logging.INFO) + handlers = [logging.StreamHandler()] + if config.get("file"): handlers.append(logging.FileHandler(config["file"])) + logging.basicConfig(level=level, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=handlers) + return logging.getLogger("ibsystem2mqtt") + +class FriendlyNames: + def __init__(self, config): self.names = config.get("friendly_names", {}) + def get_device_name(self, rs, dev_id): return self.names.get(f"rs{rs}_id{dev_id}", f"IBSystem RS{rs} ID{dev_id}") + def get_entity_name(self, rs, dev_id, path): + safe_path = re.sub(r"[^a-zA-Z0-9_]+", "_", path) + if f"rs{rs}_id{dev_id}_{safe_path}" in self.names: return self.names[f"rs{rs}_id{dev_id}_{safe_path}"] + device_name = self.names.get(f"rs{rs}_id{dev_id}") + readable = self._path_to_readable(path) + return f"{device_name} {readable}" if device_name else f"RS{rs} ID{dev_id} {readable}" + def _path_to_readable(self, path): + m = re.match(r"output\.do\.(\d+)$", path) + if m: return f"Wyjscie DO {m.group(1)}" + m = re.match(r"output\.px\.(\d+)$", path) + if m: return f"PWM {m.group(1)}" + m = re.match(r"input\.di\.(\d+)\.(a|b)$", path) + if m: return f"Wejscie DI {m.group(1)} {m.group(2).upper()}" + m = re.match(r"input\.t\.(\d+)\.value$", path) + if m: return f"Temperatura {m.group(1)}" + return path.replace(".", " ").title() + +@dataclass +class DeviceRecord: + rs: str + id: str + path: str + val: str + @property + def unique_id(self): return f"ibsystem_rs{self.rs}_id{self.id}_{re.sub(r'[^a-zA-Z0-9_]+', '_', self.path)}" + @property + def device_id(self): return f"ibsystem_rs{self.rs}_id{self.id}" + @property + def component(self): + if self.path.startswith("output.do."): return "switch" + if self.path.startswith("input.di."): return "binary_sensor" + return "sensor" + @property + def device_class(self): return "temperature" if "temp" in self.path.lower() or self.path.startswith("input.t.") else None + @property + def unit_of_measurement(self): return "C" if self.path.startswith("input.t.") and "value" in self.path else None + +class DiagnosticsHandler(BaseHTTPRequestHandler): + bridge = None + def log_message(self, format, *args): pass + def do_GET(self): + if self.path in ("/", "/status"): self._send_json(STATS.to_dict()) + elif self.path == "/health": self._send_json({"status": "ok"}) + elif self.path == "/config" and self.bridge: + cfg = json.loads(json.dumps(self.bridge.config)); cfg["mqtt"]["password"] = "***"; self._send_json(cfg) + elif self.path == "/entities" and self.bridge: + self._send_json({"count": len(self.bridge.discovered), "entities": sorted(list(self.bridge.discovered))}) + else: self.send_error(404) + def _send_json(self, data): + content = json.dumps(data, indent=2).encode() + self.send_response(200); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", len(content)); self.end_headers(); self.wfile.write(content) + +class DiagnosticsServer: + def __init__(self, host, port, logger): self.host = host; self.port = port; self.logger = logger; self.server = None + def start(self, bridge): + DiagnosticsHandler.bridge = bridge + try: + self.server = HTTPServer((self.host, self.port), DiagnosticsHandler) + threading.Thread(target=self.server.serve_forever, daemon=True).start() + self.logger.info(f"Diagnostics: http://{self.host}:{self.port}") + except Exception as e: self.logger.error(f"Diagnostics failed: {e}") + def stop(self): + if self.server: self.server.shutdown() + +class IBLSRunner: + def __init__(self, host, port, timeout_ms, logger): self.host = host; self.port = port; self.timeout_ms = timeout_ms; self.logger = logger; self._lock = threading.Lock() + def run(self, command): + cmd = f'/ibsystem/ibls -a {self.host} --pretty -p {self.port} --timeout={self.timeout_ms} -c "{command}"' + try: + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30) + if result.returncode != 0: self.logger.warning(f"ibls failed: {result.stderr}"); STATS.record_error(f"ibls: {result.stderr[:50]}"); return None + return result.stdout.strip() + except Exception as e: self.logger.error(f"ibls error: {e}"); STATS.record_error(str(e)); return None + def get_device(self, rs, dev_id, full=False): + cmd = f"get(rs.{rs}.id.{dev_id};);" if full else f"get(rs.{rs}.id.{dev_id}.input;);get(rs.{rs}.id.{dev_id}.output;);" + output = self.run(cmd) + if not output: return [] + records = [] + for line in output.splitlines(): + m = re.match(r"rs\.(\d+)\.id\.(\d+)\.(.+?)\s*=\s*(.+)", line.strip()) + if m: records.append(DeviceRecord(rs=m.group(1), id=m.group(2), path=m.group(3).strip(), val=m.group(4).strip())) + return records + def set_output(self, rs, dev_id, output_path, value): + with self._lock: return self.run(f"set(rs.{rs}.id.{dev_id}.{output_path}={value};);") is not None + +class MQTTBridge: + def __init__(self, config, ibls, logger): + self.config = config; self.ibls = ibls; self.logger = logger + self.friendly_names = FriendlyNames(config) + self.mqtt_prefix = config["mqtt"]["prefix"]; self.ha_prefix = config["mqtt"]["ha_prefix"] + self.clients = []; self.discovered = set(); self.last_published = {} + self._cmd_lock = threading.Lock(); self._last_cmd = {} + def start(self): + for broker in self.config["mqtt"]["brokers"]: + client = mqtt.Client(client_id=f"ibsystem2mqtt-{broker['name']}", protocol=mqtt.MQTTv311) + client.username_pw_set(self.config["mqtt"]["user"], self.config["mqtt"]["password"]) + client.on_message = self._on_message; client._broker_name = broker["name"] + client.will_set(f"{self.mqtt_prefix}/bridge/availability", "offline", retain=True) + try: + client.connect(broker["host"], broker["port"], 60); client.loop_start(); self.clients.append(client) + self.logger.info(f"Connected: {broker['name']} ({broker['host']})") + except Exception as e: self.logger.error(f"MQTT failed {broker['name']}: {e}"); STATS.record_error(f"MQTT: {broker['name']}") + self._publish_all(f"{self.mqtt_prefix}/bridge/availability", "online") + def stop(self): + self._publish_all(f"{self.mqtt_prefix}/bridge/availability", "offline") + for c in self.clients: c.loop_stop(); c.disconnect() + def _on_message(self, client, userdata, msg): + try: + STATS.record_command(); parts = msg.topic.split("/") + if len(parts) < 5 or parts[0] != self.mqtt_prefix or parts[3] != "set": return + rs, dev_id, target = parts[1].replace("rs",""), parts[2].replace("id",""), parts[4] + m = re.match(r"do(\d+)$", target) + if not m: return + do_num = m.group(1); payload = msg.payload.decode().strip().upper() + value = "1" if payload in ("ON","1","TRUE") else "0" if payload in ("OFF","0","FALSE") else None + if not value: return + key = (rs, dev_id, do_num, value); now = time.time() + with self._cmd_lock: + self._last_cmd = {k:v for k,v in self._last_cmd.items() if v > now-0.5} + if key in self._last_cmd: return + self._last_cmd[key] = now + self.logger.info(f"Command: rs{rs}/id{dev_id}/do{do_num} = {value}") + if self.ibls.set_output(int(rs), int(dev_id), f"setting.light.{do_num}", value): + self._publish_all(f"{self.mqtt_prefix}/rs{rs}/id{dev_id}/output/do/{do_num}/state", "ON" if value=="1" else "OFF") + except Exception as e: self.logger.error(f"Message error: {e}"); STATS.record_error(str(e)) + def poll_and_publish(self): + cfg = self.config["ibsystem"]; rs = cfg["rs"]; full_ids = set(cfg.get("full_ids", [])) + all_records = []; devices = 0 + with ThreadPoolExecutor(max_workers=self.config["polling"].get("parallel_workers", 4)) as ex: + futures = {ex.submit(self.ibls.get_device, rs, i, i in full_ids): i for i in range(1, cfg["max_ids"]+1)} + for f in as_completed(futures): + try: + recs = f.result() + if recs: devices += 1; all_records.extend(recs) + except: pass + for rec in all_records: self._publish_discovery(rec); self._publish_state(rec) + return devices + def _publish_discovery(self, rec): + if rec.unique_id in self.discovered: return + component = rec.component; state_topic = self._state_topic(rec) + payload = {"name": self.friendly_names.get_entity_name(rec.rs, rec.id, rec.path), "state_topic": state_topic, "unique_id": rec.unique_id, "availability_topic": f"{self.mqtt_prefix}/bridge/availability", "device": {"identifiers": [rec.device_id], "name": self.friendly_names.get_device_name(rec.rs, rec.id), "manufacturer": "IBSystem"}} + if component == "switch": + m = re.match(r"output\.do\.(\d+)$", rec.path) + if m: payload.update({"command_topic": f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/set/do{m.group(1)}", "payload_on": "ON", "payload_off": "OFF", "state_on": "ON", "state_off": "OFF"}) + elif component == "binary_sensor": payload.update({"payload_on": "1", "payload_off": "0"}) + elif rec.device_class: payload["device_class"] = rec.device_class + if rec.unit_of_measurement: payload["unit_of_measurement"] = rec.unit_of_measurement; payload["value_template"] = "{{ value | float / 10 }}" if rec.device_class == "temperature" else None + self._publish_all(f"{self.ha_prefix}/{component}/{rec.unique_id}/config", json.dumps(payload)) + self.discovered.add(rec.unique_id); STATS.record_entity() + def _publish_state(self, rec): + topic = self._state_topic(rec) + value = ("ON" if rec.val in ("1","true","ON") else "OFF") if rec.component == "switch" else rec.val + for c in self.clients: + cache = self.last_published.setdefault(c._broker_name, {}) + if cache.get(topic) != value: cache[topic] = value; c.publish(topic, value, retain=True) + def _state_topic(self, rec): + p = rec.path + m = re.match(r"output\.do\.(\d+)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/output/do/{m.group(1)}/state" + m = re.match(r"input\.di\.(\d+)\.(a|b)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/input/di/{m.group(1)}/{m.group(2)}" + m = re.match(r"input\.t\.(\d+)\.(\w+)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/input/t/{m.group(1)}/{m.group(2)}" + return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/{p.replace('.', '/')}" + def _publish_all(self, topic, payload, retain=True): + for c in self.clients: c.publish(topic, payload, retain=retain) + +def load_config(path=None): + if path and HAS_YAML and Path(path).exists(): + with open(path) as f: user = yaml.safe_load(f) + cfg = json.loads(json.dumps(DEFAULT_CONFIG)) + for k,v in user.items(): cfg[k].update(v) if isinstance(v,dict) and k in cfg else cfg.update({k:v}) + return cfg + return json.loads(json.dumps(DEFAULT_CONFIG)) + +def main(): + import argparse + p = argparse.ArgumentParser(); p.add_argument("-c", "--config"); p.add_argument("-v", "--verbose", action="store_true") + args = p.parse_args(); config = load_config(args.config) + if args.verbose: config["logging"]["level"] = "DEBUG" + logger = setup_logging(config["logging"]); logger.info("Starting IBSystem2MQTT v5") + ibls = IBLSRunner(config["ibsystem"]["host"], config["ibsystem"]["port"], config["ibsystem"]["timeout_ms"], logger) + bridge = MQTTBridge(config, ibls, logger) + diag = DiagnosticsServer(config["http"]["host"], config["http"]["port"], logger) if config.get("http",{}).get("enabled") else None + if diag: diag.start(bridge) + def shutdown(sig, frame): logger.info("Shutdown"); diag and diag.stop(); bridge.stop(); sys.exit(0) + signal.signal(signal.SIGINT, shutdown); signal.signal(signal.SIGTERM, shutdown) + bridge.start() + for c in bridge.clients: c.subscribe(f"{bridge.mqtt_prefix}/+/+/set/#") + interval = config["polling"]["interval_sec"] + while True: + try: + t0 = time.time(); devices = bridge.poll_and_publish(); elapsed = time.time() - t0 + STATS.record_poll(elapsed, devices); logger.debug(f"Poll: {devices} in {elapsed:.2f}s") + time.sleep(max(0, interval - elapsed)) + except Exception as e: logger.error(f"Loop error: {e}"); STATS.record_error(str(e)); time.sleep(interval) + +if __name__ == "__main__": main() diff --git a/docs/G12W_BATTERY_AUTOMATION.md b/docs/G12W_BATTERY_AUTOMATION.md new file mode 100644 index 0000000..0243801 --- /dev/null +++ b/docs/G12W_BATTERY_AUTOMATION.md @@ -0,0 +1,120 @@ +# G12W Battery Charging Automation + +## Przegląd + +Automatyzacja ładowania baterii Deye oparta na taryfie G12W z PGE. + +## Taryfa G12W - Godziny + +### Dni powszednie (Pn-Pt): +- **Tania**: 22:00-06:00 (noc) + 13:00-15:00 (dzień) +- **Droga**: 06:00-13:00 + 15:00-22:00 + +### Weekendy (Sob-Nd): +- **Tania**: 00:00-24:00 (cała doba) + +## Konfiguracja Invertera Deye + +### Programy ładowania (Time of Use - Program 1-6) + +| Program | Czas | Tryb | Prąd | Zastosowanie | +|---------|------|------|------|--------------| +| 1 | 22:00 | Grid | 80A | Nocne ładowanie (do 06:00) | +| 2 | 06:00 | Disabled | - | Stop nocnego ładowania | +| 3 | 07:00 | Disabled | - | Dzień | +| 4 | 13:00 | Grid | 100A | Dzienne ładowanie (2h okno) | +| 5 | 15:00 | Disabled | - | Stop dziennego ładowania | +| 6 | 22:00 | Grid | 80A | Reset do nocnego | + +### Ważne ustawienia + +- **Energy Pattern**: "Load First" podczas ładowania (sieć zasila dom + ładuje baterię) +- **Energy Pattern**: "Battery First" podczas normalnej pracy (bateria zasila dom) + +## Automatyzacje Home Assistant + +### Lista automatyzacji w `automations.yaml`: + +1. **g12w_night_start_charging** (20:30) + - Trigger: 20:30 dni powszednie + - Warunek: Prognoza Solcast < 6 kWh + - Akcja: Włącz Program 1 Grid 80A na 22:00 + +2. **g12w_night_stop_charging** (06:00) + - Trigger: 06:00 codziennie + - Akcja: Wyłącz ładowanie (Program 2 Disabled) + +3. **g12w_day_start_charging** (13:00) + - Trigger: 13:00 dni powszednie + - Warunek: Prognoza < 13 kWh + - Akcja: Włącz Program 4 Grid 100A + +4. **g12w_day_stop_charging** (15:00) + - Trigger: 15:00 codziennie + - Akcja: Wyłącz ładowanie (Program 5 Disabled) + +5. **g12w_weekend_charging** (Sob-Nd) + - Trigger: Sobota/Niedziela + - Akcja: Ładowanie całodobowe + +6. **g12w_very_cloudy** (bardzo pochmurnie) + - Warunek: Prognoza < 3 kWh + - Akcja: Maksymalne ładowanie + +## Entity IDs (Deye via Solarman) + +```yaml +# Odczyt +- sensor.deye_battery_soc # Stan naładowania % +- sensor.deye_battery_power # Moc ładowania/rozładowania W + +# Sterowanie programami +- select.deye_time_of_use_sellX_mode # Grid/Disabled/Battery +- number.deye_time_of_use_sellX_time # Godzina startu +- number.deye_time_of_use_sellX_current # Prąd ładowania A + +# Wzorzec energii +- select.deye_energy_pattern # Load First / Battery First +``` + +## Progi ładowania (Solcast forecast) + +| Prognoza | Ładowanie nocne | Ładowanie dzienne | +|----------|-----------------|-------------------| +| < 3 kWh | 100% SOC (80A) | Tak (100A) | +| 3-6 kWh | 80% SOC (80A) | Tak (100A) | +| 6-13 kWh | Nie | Tak (100A) | +| > 13 kWh | Nie | Nie | + +## Diagnostyka + +### Bateria nie ładuje się: +1. Sprawdź Energy Pattern = "Load First" +2. Sprawdź aktywny program (chronologicznie pierwszy po aktualnej godzinie) +3. Sprawdź prąd ładowania > 0 +4. Sprawdź tryb = "Grid" (nie "Disabled") + +### Konflikt programów: +- Programy działają chronologicznie +- Program z najbliższą przyszłą godziną jest aktywny +- Dwa programy z tą samą godziną → tylko jeden aktywny + +## SSH Access + +```bash +# Home Assistant .151 +ssh -p 2222 root@192.168.50.151 +# Password: QWer!@34 + +# Pliki konfiguracyjne +cd /config +vi automations.yaml +ha automation reload +``` + +## Historia zmian + +- 2025-02-10: Fix Program 6 blokującego 21-22, zmiana na 22:00 +- 2025-02-10: Zmiana trigger z 21:30 na 20:30 +- 2025-02-10: Ustawienie 80A noc, 100A dzień +- 2025-02-10: Dodanie automatyzacji stop 06:00 i 15:00 diff --git a/docs/IBSYSTEM_MQTT_INTEGRATION.md b/docs/IBSYSTEM_MQTT_INTEGRATION.md new file mode 100644 index 0000000..b5bde6e --- /dev/null +++ b/docs/IBSYSTEM_MQTT_INTEGRATION.md @@ -0,0 +1,182 @@ +# IBSystem MQTT Integration + +## Przegląd + +Integracja sterowników IBSystem z Home Assistant przez MQTT. + +## Architektura + +``` +IBSystem RS485 → ibsystem2mqtt_v5.py → Mosquitto MQTT → Home Assistant + ↓ ↓ +192.168.50.243 MQTT Discovery +``` + +## Komponenty + +### ibsystem2mqtt_v5.py + +Lokalizacja: `/ibsystem/ibsystem2mqtt_v5.py` na 192.168.50.243 + +Funkcje: +- Połączenie TCP z IBSystem (port 9761) +- Parsowanie odpowiedzi protokołu +- Publikacja stanów do MQTT +- Obsługa komend sterujących + +### Konfiguracja + +Plik: `/ibsystem/config.yaml` + +```yaml +mqtt: + host: 192.168.50.151 + port: 1883 + username: mqtt_user + password: mqtt_password + +ibsystem: + host: 127.0.0.1 + port: 9761 + +modules: + - rs: 0 + id: 35 + name: "ID35" +``` + +## MQTT Topics + +### Discovery (automatyczne tworzenie encji w HA) + +``` +homeassistant/switch/ibsystem_rs0_id35_doX/config +homeassistant/sensor/ibsystem_rs0_id35_tempX/config +``` + +### Stany (publikowane przez skrypt) + +``` +ibsystem/rs0/id35/state # JSON ze stanami wszystkich wyjść +ibsystem/rs0/id35/doX # Stan pojedynczego wyjścia (ON/OFF) +``` + +### Komendy (odbierane przez skrypt) + +``` +ibsystem/rs0/id35/set/doX # Payload: ON lub OFF +``` + +## Sterowanie wyjściami + +### Prawidłowa komenda (setting.light.X) + +``` +set(rs.0.id.35.setting.light.1=1;); # Włącz DO1 +set(rs.0.id.35.setting.light.1=0;); # Wyłącz DO1 +``` + +### BŁĘDNA komenda (output.do.X) - tylko odczyt! + +``` +# NIE UŻYWAĆ - to jest read-only! +set(rs.0.id.35.output.do.1=1;); +``` + +## Entity IDs w Home Assistant + +### Prawidłowe (MQTT Discovery): +``` +switch.ibsystem_rs0_id35_rs0_id35_output_do_1 +switch.ibsystem_rs0_id35_rs0_id35_output_do_2 +... +``` + +### Stare (nie działają): +``` +switch.sterownik_35_do1 # Brak command_topic +switch.sterownik_35_do2 +``` + +## Dashboard + +Plik: `/config/dashboards/id35_dashboard.yaml` + +Encje muszą używać prawidłowych MQTT entity IDs: +```yaml +entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "Wyjście DO1" +``` + +## Watchdog + +Skrypt: `/tmp/watchdog.sh` (kopia w repo: `ibsystem/ibsystem2mqtt_watchdog.sh`) + +```bash +#!/bin/bash +# Sprawdza health endpoint co minutę +# Restartuje po 3 nieudanych próbach + +HEALTH_URL="http://127.0.0.1:8080/health" +FAILURE_FILE="/tmp/ibsystem2mqtt_failures" +MAX_FAILURES=3 + +# Check health +if curl -s --max-time 5 "$HEALTH_URL" > /dev/null; then + echo 0 > "$FAILURE_FILE" +else + FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo 0) + FAILURES=$((FAILURES + 1)) + echo $FAILURES > "$FAILURE_FILE" + + if [ $FAILURES -ge $MAX_FAILURES ]; then + systemctl restart ibsystem2mqtt + echo 0 > "$FAILURE_FILE" + fi +fi +``` + +### Cron + +```bash +* * * * * /tmp/watchdog.sh +``` + +## Diagnostyka + +### Sprawdź czy MQTT działa: + +```bash +# Na 192.168.50.243 +mosquitto_sub -h 192.168.50.151 -t "ibsystem/#" -v + +# Wyślij komendę testową +mosquitto_pub -h 192.168.50.151 -t "ibsystem/rs0/id35/set/do1" -m "ON" +``` + +### Sprawdź logi serwisu: + +```bash +journalctl -u ibsystem2mqtt -f +``` + +### Restart serwisu: + +```bash +systemctl restart ibsystem2mqtt +``` + +## SSH Access + +```bash +ssh wk@192.168.50.243 +# Password: QWer1234 +cd /ibsystem +``` + +## Historia zmian + +- 2025-02-10: Fix sterowania przełącznikami (setting.light.X zamiast output.do.X) +- 2025-02-10: Aktualizacja dashboardu do MQTT entity IDs +- 2025-02-10: Dodanie watchdog dla ibsystem2mqtt diff --git a/ha-configs/151/automations.yaml b/ha-configs/151/automations.yaml new file mode 100644 index 0000000..d50298b --- /dev/null +++ b/ha-configs/151/automations.yaml @@ -0,0 +1,612 @@ +- alias: Solcast - Raport poranny + description: Dzienny raport o 7:00 + trigger: + - platform: time + at: 07:00:00 + action: + - service: notify.persistent_notification + data: + title: ☀️ Prognoza PV + message: 'Dziś: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh + + Jutro: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh + + Bateria: {{ states(''sensor.inverter_deye_battery'') }}% + + ' + +- alias: Solcast G12W - Bardzo pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 + 13:00-15:00 gdy < 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + below: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - start dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + # Program 5: 15:00 - koniec dziennego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 (żeby nie blokował) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌧️ G12W: Bardzo pochmurnie!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A) + 13:00-15:00 (100A)!' + +- alias: Solcast G12W - Pochmurnie (Pn-Czw) + description: Ładowanie 22:00-06:00 gdy >= 3 kWh + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_jutro + above: 3 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 95 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 - start nocnego ładowania + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: kontynuacja + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 - koniec + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4: 13:00 - dzienne disabled (słonecznie) + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 15 + # Program 5: 15:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_5_soc + data: + value: 15 + # Program 6: 22:00 - taki sam jak Program 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⛅ G12W: Pochmurnie' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_jutro'') }} kWh. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Piątek wyłącz (sobota tania) + description: Piątek wieczór - wyłącz ładowanie bo sobota jest tania cała doba + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🎉 G12W: Weekend startuje!' + message: Sobota i niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Sobota bez zmian + description: Sobota - niedziela też jest tania + trigger: + - platform: time + at: '21:30:00' + condition: + - condition: time + weekday: + - sat + action: + - service: notify.persistent_notification + data: + title: '🎉 G12W: Niedziela też tania!' + message: Niedziela = tania energia całą dobę. + +- alias: Solcast G12W - Niedziela ładowanie na poniedziałek + description: Niedziela 20:30 - ustaw ładowanie 22:00-06:00 na poniedziałek + trigger: + - platform: time + at: '20:30:00' + condition: + - condition: time + weekday: + - sun + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + # Program 1: 22:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_1_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_1_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_1_soc + data: + value: 100 + # Program 2: 00:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_2_time + data: + time: '00:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_2_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_2_soc + data: + value: 100 + # Program 3: 06:00 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_3_time + data: + time: '06:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_3_charging + data: + option: Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_program_3_soc + data: + value: 15 + # Program 4,5 - dzienne disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_4_time + data: + time: '13:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: time.set_value + target: + entity_id: time.inverter_deye_program_5_time + data: + time: '15:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_5_charging + data: + option: Disabled + # Program 6: 22:00 - tak samo jak 1 + - service: time.set_value + target: + entity_id: time.inverter_deye_program_6_time + data: + time: '22:00:00' + - service: select.select_option + target: + entity_id: select.inverter_deye_program_6_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_6_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '🌙 G12W: Ładowanie na poniedziałek' + message: 'Bateria: {{ states(''sensor.inverter_deye_battery'') }}%. Ładowanie 22:00-06:00 (80A).' + +- alias: Solcast G12W - Wyłącz ładowanie 06:00 + description: Wyłącza ładowanie po taniej strefie nocnej + trigger: + - platform: time + at: '06:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + - service: notify.persistent_notification + data: + title: '🌅 G12W: Koniec ładowania nocnego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Włącz ładowanie dzienne 13:00 + description: Włącza ładowanie w taniej strefie dziennej gdy prognoza < 5 kWh (100A) + trigger: + - platform: time + at: '12:55:00' + condition: + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: numeric_state + entity_id: sensor.solcast_pv_forecast_prognoza_na_dzisiaj + below: 5 + - condition: numeric_state + entity_id: sensor.inverter_deye_battery + below: 80 + action: + - service: switch.turn_on + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Load First + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 100 + # Program 4: 13:00 - Grid + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Grid + - service: number.set_value + target: + entity_id: number.inverter_deye_program_4_soc + data: + value: 100 + - service: notify.persistent_notification + data: + title: '⚡ G12W: Ładowanie dzienne (100A)!' + message: 'Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh, SOC: {{ states(''sensor.inverter_deye_battery'') }}%' + +- alias: Solcast G12W - Wyłącz ładowanie dzienne 15:00 + description: Wyłącza ładowanie po taniej strefie dziennej + trigger: + - platform: time + at: '15:00:00' + condition: + - condition: state + entity_id: switch.inverter_deye_battery_grid_charging + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + action: + - service: switch.turn_off + target: + entity_id: switch.inverter_deye_battery_grid_charging + - service: select.select_option + target: + entity_id: select.inverter_deye_energy_pattern + data: + option: Battery First + # Reset prądu do 80A i Program 4 do Disabled + - service: number.set_value + target: + entity_id: number.inverter_deye_battery_grid_charging_current + data: + value: 80 + - service: select.select_option + target: + entity_id: select.inverter_deye_program_4_charging + data: + option: Disabled + - service: notify.persistent_notification + data: + title: '🔋 G12W: Koniec ładowania dziennego' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}%. Prąd: 80A.' + +- alias: Solcast G12W - Raport poranny + description: Status o 6:05 + trigger: + - platform: time + at: 06:05:00 + action: + - service: notify.persistent_notification + data: + title: '🌅 G12W: Raport poranny' + message: 'SOC: {{ states(''sensor.inverter_deye_battery'') }}% + + Naładowano: {{ states(''sensor.inverter_deye_today_battery_charge'') }} kWh + + Prognoza: {{ states(''sensor.solcast_pv_forecast_prognoza_na_dzisiaj'') }} kWh' + +- id: heating_curve_auto_adjust + alias: Ogrzewanie - Auto dostosowanie krzywej + description: Automatycznie dostosowuje krzywą grzewczą + trigger: + - platform: state + entity_id: sensor.temperatura_w_salonie + for: + minutes: 10 + - platform: time_pattern + hours: /1 + condition: + - condition: template + value_template: '{% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {{ diff | abs > 0.3 }}' + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current_t1s = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {% set diff = states(''sensor.roznica_temperatury_2'') | float(0) %} {% set adjustment = (diff * 350) | int %} {% set new_t1s = current_t1s - adjustment %} {% if new_t1s < 5000 %}5000{% elif new_t1s > 7000 %}7000{% else %}{{ new_t1s }}{% endif %}' + mode: single + +- id: heating_temp_limit_enforcer + alias: Ogrzewanie - Wymuszenie limitu temperatury + description: Obniża T1S gdy temperatura przekracza 35°C + trigger: + - platform: numeric_state + entity_id: sensor.aktualna_nastawa_temperatury_2 + above: 35 + for: + minutes: 5 + action: + - service: shell_command.set_heating_t1s + data: + value: '{% set current = states(''sensor.sterownik_1_rs0_id1_setting_t1s'') | int(7215) %} {{ current - 500 }}' + - service: persistent_notification.create + data: + message: Temperatura przekroczyła limit 35°C. Obniżam T1S. + mode: single + +- id: defrost_counter + alias: Licznik defrostów + description: Zlicza ilość defrostów pompy + trigger: + - platform: state + entity_id: binary_sensor.pompa_defrost + from: 'off' + to: 'on' + action: + - service: counter.increment + target: + entity_id: counter.defrost_count + mode: single diff --git a/ha-configs/151/configuration.yaml b/ha-configs/151/configuration.yaml new file mode 100644 index 0000000..23eebe5 --- /dev/null +++ b/ha-configs/151/configuration.yaml @@ -0,0 +1,200 @@ +# Home Assistant Configuration + + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy + +# Lovelace dashboards - mode: storage pozwala na UI dashboardy +lovelace: + mode: storage + resources: + - url: /hacsfiles/sunsynk-power-flow-card/sunsynk-power-flow-card.js + type: module + dashboards: + lovelace-heating: + mode: yaml + title: Ogrzewanie + icon: mdi:fire + show_in_sidebar: true + filename: dashboards/heating_dashboard.yaml + lovelace-ibsystem: + mode: yaml + title: IBSystem + icon: mdi:heat-pump + show_in_sidebar: true + filename: dashboards/ibsystem_dashboard.yaml + lovelace-id35: + mode: yaml + title: ID35 + icon: mdi:thermometer + show_in_sidebar: true + filename: dashboards/id35_dashboard.yaml + + +homeassistant: + name: Home + latitude: 50.0 + longitude: 19.0 + elevation: 200 + unit_system: metric + time_zone: Europe/Warsaw + +# Configure a default setup +default_config: + +# Text to speech +tts: + - platform: google_translate + +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# Shell commands +shell_command: + set_heating_t1s: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 --pretty -p 2001 -c 'set(rs.0.id.1.setting.t1s={{ value }};);'" + +# Template sensors +template: + - sensor: + # Temperatura w salonie + - name: "Temperatura w salonie" + unique_id: temperatura_salonu_ibsystem + state: > + {% set raw = states('sensor.sterownik_4_rs0_id4_input_t_0_value') %} + {% if raw not in ['unknown', 'unavailable', ''] %} + {{ (raw | float(0) / 10) | round(1) }} + {% else %} + {{ states('sensor.temperatura_w_salonie') | float(23.3) }} + {% endif %} + unit_of_measurement: "°C" + device_class: temperature + state_class: measurement + + # Różnica temperatury (docelowa - aktualna) + - name: "Różnica temperatury 2" + unique_id: roznica_temperatury_2 + state: > + {% set current = states('sensor.temperatura_w_salonie') | float(22) %} + {% set target = states('input_number.heating_target_temp') | float(22) %} + {{ (target - current) | round(1) }} + unit_of_measurement: "°C" + state_class: measurement + + + # Moc aktualna pompy (instantaneous) + - name: "Moc pompy aktualna" + unique_id: moc_pompy_aktualna + state: > + {% set current = states('sensor.sterownik_1_rs0_id1_outdoor_unit_current') | float(0) %} + {% set voltage = states('sensor.sterownik_1_rs0_id1_outdoor_unit_voltage') | float(230) %} + {{ ((current * voltage) / 1000) | round(2) }} + unit_of_measurement: "kW" + device_class: power + state_class: measurement + + # COP chwilowy (instantaneous COP) + - name: "COP aktualny" + unique_id: cop_aktualny_pompy + state: > + {% set power_in = states('sensor.moc_pompy_aktualna') | float(0) %} + {% set freq = states('sensor.sterownik_1_rs0_id1_operating_frequency') | int(0) %} + {% set t1 = states('sensor.sterownik_1_rs0_id1_t1') | float(0) %} + {% set t4 = states('sensor.sterownik_1_rs0_id1_t4') | float(0) %} + {% if power_in > 0.1 and freq > 0 %} + {# Przybliżona moc cieplna: power_in * estymowany COP #} + {# COP zależy od różnicy temp: im większa różnica, tym niższy COP #} + {% set delta_t = t1 - t4 %} + {% set estimated_cop = 5.0 - (delta_t * 0.05) %} + {% set estimated_cop = [estimated_cop, 2.0] | max %} + {% set estimated_cop = [estimated_cop, 6.0] | min %} + {{ estimated_cop | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # SCOP (seasonal COP - z liczników) + - name: "SCOP pompy" + unique_id: scop_pompy_sezonowy + state: > + {% set consumed = states('sensor.sterownik_1_rs0_id1_electricity_consumption_lo') | float(1) %} + {% set produced = states('sensor.sterownik_1_rs0_id1_power_output_lo') | float(0) %} + {% if consumed > 0 %} + {{ (produced / consumed) | round(2) }} + {% else %} + 0 + {% endif %} + state_class: measurement + + # Licznik defrostów + - name: "Defrost aktywny" + unique_id: defrost_status + state: > + {% set load = states('sensor.sterownik_1_rs0_id1_load_output') | int(0) %} + {% set defrost_bit = (load // 32768) % 2 %} + {{ 'ON' if defrost_bit == 1 else 'OFF' }} + + - binary_sensor: + # Binary sensor defrost + - name: "Pompa defrost" + unique_id: pompa_defrost_binary + state: > + {{ states('sensor.defrost_aktywny') == 'ON' }} + device_class: running + +# Input number dla celu temperatury +input_number: + heating_target_temp: + name: Docelowa temperatura + initial: 22 + min: 18 + max: 25 + step: 0.5 + unit_of_measurement: "°C" + icon: mdi:thermometer + +# Counter dla liczenia defrostów +counter: + defrost_count: + name: Liczba defrostów + icon: mdi:snowflake-melt + initial: 0 + step: 1 + + +utility_meter: + energia_pobrana_dzienna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (dzienna) + cycle: daily + + energia_pobrana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (tygodniowa) + cycle: weekly + + energia_pobrana_miesieczna: + source: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Energia pobrana (miesięczna) + cycle: monthly + + energia_oddana_dzienna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (dzienna) + cycle: daily + + energia_oddana_tygodniowa: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (tygodniowa) + cycle: weekly + + energia_oddana_miesieczna: + source: sensor.sterownik_1_rs0_id1_power_output_lo + name: Energia oddana (miesięczna) + cycle: monthly + + set_heating_curve: > + sshpass -p 'QWer1234' ssh -o StrictHostKeyChecking=no wk@192.168.50.243 + "/ibsystem/ibls -a 127.0.0.1 -p 2001 -c 'set(rs.0.id.1.curve.selection={{ curve }};);' 2>/dev/null" diff --git a/ha-configs/151/dashboards/deye_dashboard.yaml b/ha-configs/151/dashboards/deye_dashboard.yaml new file mode 100644 index 0000000..7975163 --- /dev/null +++ b/ha-configs/151/dashboards/deye_dashboard.yaml @@ -0,0 +1,62 @@ +title: Deye & Bateria +icon: mdi:solar-power +path: deye + +views: + - title: Przegląd + cards: + - type: entities + title: 🔋 Bateria + entities: + - entity: sensor.inverter_deye_battery + name: Stan baterii (SOC) + - entity: switch.inverter_deye_battery_grid_charging + name: Ładowanie z sieci + - entity: number.inverter_deye_battery_grid_charging_current + name: Prąd ładowania + + - type: entities + title: ⚡ Program 1 + entities: + - entity: time.inverter_deye_program_1_time + name: Czas startu + - entity: select.inverter_deye_program_1_charging + name: Tryb + - entity: number.inverter_deye_program_1_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 2 + entities: + - entity: time.inverter_deye_program_2_time + name: Czas startu + - entity: select.inverter_deye_program_2_charging + name: Tryb + - entity: number.inverter_deye_program_2_soc + name: Docelowy SOC + + - type: entities + title: ⚡ Program 3 + entities: + - entity: time.inverter_deye_program_3_time + name: Czas startu + - entity: select.inverter_deye_program_3_charging + name: Tryb + - entity: number.inverter_deye_program_3_soc + name: Docelowy SOC + + - type: entities + title: ☀️ Solcast - Prognoza + entities: + - entity: sensor.solcast_pv_forecast_prognoza_na_jutro + name: Prognoza na jutro + - entity: sensor.solcast_pv_forecast_forecast_today + name: Prognoza dziś + - entity: sensor.solcast_pv_forecast_forecast_tomorrow + name: Prognoza jutro (kWh) + + - type: history-graph + title: Historia baterii + hours_to_show: 24 + entities: + - entity: sensor.inverter_deye_battery diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml b/ha-configs/151/dashboards/heating_dashboard.yaml new file mode 100644 index 0000000..8f42106 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml @@ -0,0 +1,130 @@ +title: Ogrzewanie - Pompa Ciepła +views: + - title: Główny + path: default_view + cards: + - type: entities + title: 🌡️ Temperatury + entities: + - entity: sensor.temperatura_w_salonie + name: 🏠 Temperatura w salonie + - entity: input_number.heating_target_temp + name: 🎯 Temperatura docelowa + - entity: sensor.sterownik_1_rs0_id1_t4 + name: ❄️ Temperatura zewnętrzna (T4) + - entity: sensor.sterownik_1_rs0_id1_t1 + name: 🔥 Temperatura zasilania (T1) + - entity: sensor.sterownik_1_rs0_id1_t5 + name: 🚿 Temperatura CWU (T5) + + - type: entities + title: ⚙️ Parametry krzywej grzewczej + entities: + - entity: sensor.sterownik_1_rs0_id1_setting_t1s + name: T1S (parametr) + - entity: sensor.sterownik_1_rs0_id1_climate_curve_t1s_calculated_value_1 + name: Temp obliczona krzywą + - entity: sensor.sterownik_1_rs0_id1_function_setting + name: Status (4096=ON) + + - type: entities + title: ⚡ Moc i efektywność + entities: + - entity: sensor.moc_pompy_aktualna + name: Moc aktualna (kW) + - entity: sensor.cop_aktualny + name: COP chwilowy + - entity: sensor.scop_pompy + name: SCOP sezonowy + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_current + name: Prąd (A) + - entity: sensor.sterownik_1_rs0_id1_outdoor_unit_voltage + name: Napięcie (V) + - entity: sensor.sterownik_1_rs0_id1_operating_frequency + name: Częstotliwość (Hz) + + - type: entities + title: 📊 Liczniki energii - DZIŚ + entities: + - entity: sensor.energia_pobrana_dzienna + name: ⚡ Pobrana dzisiaj + - entity: sensor.energia_oddana_dzienna + name: 🔥 Oddana dzisiaj + - type: divider + - entity: sensor.sterownik_1_rs0_id1_electricity_consumption_lo + name: Pobrana TOTAL + - entity: sensor.sterownik_1_rs0_id1_power_output_lo + name: Oddana TOTAL + + - type: entities + title: 📈 Liczniki energii - TYDZIEŃ / MIESIĄC + entities: + - entity: sensor.energia_pobrana_tygodniowa + name: ⚡ Pobrana (tydzień) + - entity: sensor.energia_oddana_tygodniowa + name: 🔥 Oddana (tydzień) + - type: divider + - entity: sensor.energia_pobrana_miesieczna + name: ⚡ Pobrana (miesiąc) + - entity: sensor.energia_oddana_miesieczna + name: 🔥 Oddana (miesiąc) + + - type: entities + title: ❄️ Defrost + entities: + - entity: binary_sensor.pompa_defrost + name: Defrost aktywny + - entity: counter.defrost_count + name: Liczba defrostów + + - type: history-graph + title: 📈 Historia temperatur (24h) + hours_to_show: 24 + entities: + - entity: sensor.temperatura_w_salonie + name: Salon + - entity: sensor.sterownik_1_rs0_id1_t4 + name: Zewnątrz + - entity: sensor.sterownik_1_rs0_id1_t1 + name: Zasilanie + + - type: history-graph + title: 📈 COP i moc (24h) + hours_to_show: 24 + entities: + - entity: sensor.cop_aktualny + name: COP + - entity: sensor.scop_pompy + name: SCOP + - entity: sensor.moc_pompy_aktualna + name: Moc (kW) + + - type: markdown + content: | + ## 📝 Wyjaśnienia + + **Liczniki energii:** + - Resetują się codziennie o 00:00 (dzienny) + - Resetują się w poniedziałek (tygodniowy) + - Resetują się 1. dnia miesiąca (miesięczny) + + **COP chwilowy vs SCOP:** + - COP: efektywność w danej chwili + - SCOP: średnia od początku sezonu + + **Defrost:** + - Normalne gdy temp < 5°C + - Zbyt często (>10/dzień) = problem + + **T1S (parametr krzywej):** + - Zarządzany AUTOMATYCZNIE przez pompę + - HA dostosowuje co 2h jeśli potrzeba + + --- + + ### 📊 Stan: + - Salon: {{ states('sensor.temperatura_w_salonie') }}°C + - Cel: {{ states('input_number.heating_target_temp') }}°C + - T1: {{ states('sensor.sterownik_1_rs0_id1_t1') }}°C + - COP: {{ states('sensor.cop_aktualny') }} + - Dziś pobrano: {{ states('sensor.energia_pobrana_dzienna') }} kWh diff --git a/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 new file mode 100644 index 0000000..8687ee5 --- /dev/null +++ b/ha-configs/151/dashboards/heating_dashboard.yaml.backup_20260208_212154 @@ -0,0 +1,189 @@ +title: Ogrzewanie +views: + - title: Krzywa grzewcza + path: heating + icon: mdi:chart-bell-curve-cumulative + cards: + - type: markdown + content: | + # 🏠 System ogrzewania - monitoring + + - type: conditional + conditions: + - condition: numeric_state + entity: sensor.aktualna_nastawa_temperatury_2 + above: 35 + card: + type: markdown + content: | + ## 🔴 ALARM! Temperatura przekracza 35°C! + Temperatura zasilania: **{{ states('sensor.aktualna_nastawa_temperatury_2') }}°C** + + System automatycznie obniży nastawę. + + - type: vertical-stack + cards: + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.temperatura_wewnetrzna_2 + name: W domu + min: 18 + max: 26 + severity: + green: 21 + yellow: 20 + red: 19 + needle: true + - type: gauge + entity: sensor.aktualna_nastawa_temperatury_2 + name: Temp zasilania + min: 20 + max: 50 + severity: + green: 25 + yellow: 33 + red: 35 + needle: true + segments: + - from: 0 + color: "#4caf50" + label: OK + - from: 33 + color: "#ffc107" + label: Uwaga + - from: 35 + color: "#db4437" + label: LIMIT! + + - type: entities + title: "🎯 Ustawienia" + entities: + - entity: input_number.heating_target_temp + name: "Temperatura docelowa w domu" + - entity: input_number.heating_max_temp + name: "🔒 MAX temperatura zasilania" + + - type: entities + title: "⚠️ Status zabezpieczeń" + entities: + - entity: sensor.alert_przekroczenie_temperatury_2 + name: "Status temperatury" + + - type: markdown + content: | + ## 📊 COP - Wydajność energetyczna + + - type: horizontal-stack + cards: + - type: gauge + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP aktualny" + min: 0 + max: 6 + severity: + red: 0 + yellow: 2.5 + green: 3.5 + needle: true + - type: statistic + entity: sensor.cop_wydajnosc_pompy_2 + name: "COP średni (1h)" + stat_type: mean + period: + calendar: + period: hour + + - type: entities + title: "💡 Sugestie optymalizacji COP" + entities: + - entity: sensor.cop_wskaznik_jakosci_2 + name: "📊 Ocena wydajności" + - entity: sensor.cop_sugestie_optymalizacji_2 + name: "💡 Rekomendacje" + + - type: entities + title: "⚡ Pompa ciepła - stan" + entities: + - entity: sensor.tryb_pracy_pompy + name: "🔧 Tryb pracy" + - entity: sensor.cop_wydajnosc_pompy_2 + name: "📊 COP" + - entity: sensor.moc_wyjsciowa_pompy_2 + name: "🔥 Moc cieplna" + - entity: sensor.moc_wejsciowa_pompy_2 + name: "⚡ Pobór energii" + - entity: sensor.czas_pracy_sprezarki + name: "⏱️ Czas pracy sprężarki" + + - type: entities + title: "🌡️ Temperatury" + show_header_toggle: false + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: "🏠 W domu" + - entity: sensor.temperatura_zewnetrzna_2 + name: "🌡️ Na zewnątrz" + - type: divider + - entity: sensor.aktualna_nastawa_temperatury_2 + name: "📍 Aktualna nastawa zasilania" + - entity: sensor.temperatura_zasilania_t1s_2 + name: "🎯 T1S - Nastawa docelowa" + - entity: sensor.temperatura_zasilania_rzeczywista + name: "💧 Rzeczywista temp wody" + - type: divider + - entity: sensor.roznica_temperatury + name: "📈 Różnica od celu" + + - type: history-graph + title: "📈 COP - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.cop_wydajnosc_pompy_2 + name: COP + + - type: history-graph + title: "🌡️ Temperatury - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.temperatura_wewnetrzna_2 + name: W domu + - entity: sensor.aktualna_nastawa_temperatury_2 + name: Nastawa zasilania + - entity: sensor.temperatura_zewnetrzna_2 + name: Zewnątrz + + - type: history-graph + title: "⚡ Moc - Historia 24h" + hours_to_show: 24 + entities: + - entity: sensor.moc_wyjsciowa_pompy_2 + name: Moc cieplna (kW) + - entity: sensor.moc_wejsciowa_pompy_2 + name: Pobór energii (kW) + + - type: entities + title: "⚙️ Automatyzacje" + entities: + - entity: automation.ogrzewanie_auto_dostosowanie_z_limitem + name: "🤖 Auto dostosowanie (z limitem)" + - entity: automation.ogrzewanie_wymuszenie_limitu_temperatury + name: "🔒 Wymuszenie limitu 35°C" + + - type: markdown + content: | + ## 🔒 Zabezpieczenie temperatury + + **Maksymalna temperatura zasilania: 35°C** + + System automatycznie: + - ✅ Monitoruje temperaturę co minutę + - ✅ Blokuje wzrost powyżej 35°C + - ✅ Automatycznie obniża gdy przekroczone + - ✅ Wysyła powiadomienia + + **Dlaczego 35°C?** + - Optymalne dla COP (>3.5) + - Wystarczające dla ogrzewania podłogowego + - Bezpieczne dla instalacji + - Efektywne energetycznie diff --git a/ha-configs/151/dashboards/ibsystem_dashboard.yaml b/ha-configs/151/dashboards/ibsystem_dashboard.yaml new file mode 100644 index 0000000..ca1aba6 --- /dev/null +++ b/ha-configs/151/dashboards/ibsystem_dashboard.yaml @@ -0,0 +1,246 @@ +title: IBSystem Sterowniki +views: + - title: Wszystkie sterowniki + path: all + icon: mdi:lightbulb-group + cards: + - type: horizontal-stack + cards: + - type: button + name: "WSZYSTKIE ON" + icon: mdi:lightbulb-group + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_on + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + - type: button + name: "WSZYSTKIE OFF" + icon: mdi:lightbulb-group-off + icon_height: 40px + tap_action: + action: call-service + service: switch.turn_off + target: + entity_id: + - switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + - switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + - switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + - switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + - switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + - switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + - switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + - switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + - switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + - switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + - switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + - switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + + - type: entities + title: "ID30" + entities: + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id30_rs0_id30_output_do_3 + name: "DO.3" + + - type: entities + title: "ID31" + entities: + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id31_rs0_id31_output_do_3 + name: "DO.3" + + - type: entities + title: "ID32" + entities: + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id32_rs0_id32_output_do_3 + name: "DO.3" + + - type: entities + title: "ID33" + entities: + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id33_rs0_id33_output_do_3 + name: "DO.3" + + - type: entities + title: "ID34" + entities: + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id34_rs0_id34_output_do_3 + name: "DO.3" + + - type: entities + title: "ID35" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "ID36" + entities: + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id36_rs0_id36_output_do_3 + name: "DO.3" + + - type: entities + title: "ID37" + entities: + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id37_rs0_id37_output_do_3 + name: "DO.3" + + - type: entities + title: "ID38" + entities: + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id38_rs0_id38_output_do_3 + name: "DO.3" + + - type: entities + title: "ID39" + entities: + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id39_rs0_id39_output_do_3 + name: "DO.3" + + - type: entities + title: "ID40" + entities: + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id40_rs0_id40_output_do_3 + name: "DO.3" diff --git a/ha-configs/151/dashboards/id35_dashboard.yaml b/ha-configs/151/dashboards/id35_dashboard.yaml new file mode 100644 index 0000000..404f9cd --- /dev/null +++ b/ha-configs/151/dashboards/id35_dashboard.yaml @@ -0,0 +1,33 @@ +title: IBSystem ID35 +views: + - title: Sterownik ID35 + path: id35 + icon: mdi:lightbulb-group + cards: + - type: markdown + content: | + ## Sterowanie oświetleniem ID35 + + - type: entities + title: "Wyjścia cyfrowe (MQTT)" + entities: + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_0 + name: "DO.0" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_1 + name: "DO.1" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_2 + name: "DO.2" + - entity: switch.ibsystem_rs0_id35_rs0_id35_output_do_3 + name: "DO.3" + + - type: entities + title: "Wejścia cyfrowe" + entities: + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_a + name: "DI.0.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_0_b + name: "DI.0.B" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_a + name: "DI.1.A" + - entity: binary_sensor.sterownik_35_rs0_id35_input_di_1_b + name: "DI.1.B" diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml new file mode 100644 index 0000000..48a6859 --- /dev/null +++ b/ibsystem/config.yaml @@ -0,0 +1,30 @@ +mqtt: + brokers: + - name: "ha" + host: "192.168.50.151" + port: 1883 + user: "mqtt" + password: "mqtt123" + prefix: "ibsystem" + ha_prefix: "homeassistant" + +ibsystem: + host: "127.0.0.1" + port: 2001 + rs: 0 + max_ids: 40 + full_ids: [1] + timeout_ms: 10000 + +polling: + interval_sec: 2.0 + parallel_workers: 8 + +logging: + level: "DEBUG" + file: "/tmp/ibsystem2mqtt.log" + +http: + enabled: true + host: "0.0.0.0" + port: 8080 diff --git a/ibsystem/ibsystem2mqtt_v5.py b/ibsystem/ibsystem2mqtt_v5.py new file mode 100755 index 0000000..5650a9a --- /dev/null +++ b/ibsystem/ibsystem2mqtt_v5.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +"""IBSystem -> MQTT Bridge for Home Assistant v5""" +import subprocess, re, time, json, threading, logging, signal, sys +from pathlib import Path +from concurrent.futures import ThreadPoolExecutor, as_completed +from dataclasses import dataclass +from http.server import HTTPServer, BaseHTTPRequestHandler +from datetime import datetime +import paho.mqtt.client as mqtt +try: + import yaml + HAS_YAML = True +except ImportError: + HAS_YAML = False + +DEFAULT_CONFIG = {"mqtt": {"brokers": [{"name": "ha", "host": "ha.local", "port": 1883}], "user": "mqtt", "password": "mqtt123", "prefix": "ibsystem", "ha_prefix": "homeassistant"}, "ibsystem": {"host": "127.0.0.1", "port": 2001, "rs": 0, "max_ids": 40, "full_ids": [1], "timeout_ms": 10000}, "polling": {"interval_sec": 2.0, "parallel_workers": 8}, "logging": {"level": "INFO", "file": None}, "http": {"enabled": True, "host": "0.0.0.0", "port": 8080}, "friendly_names": {}} + +class BridgeStats: + def __init__(self): + self.start_time = datetime.now() + self.poll_cycles = 0 + self.last_poll_time = None + self.last_poll_duration = 0.0 + self.devices_discovered = 0 + self.entities_discovered = 0 + self.messages_received = 0 + self.commands_executed = 0 + self.errors = 0 + self.last_error = None + self._lock = threading.Lock() + def record_poll(self, duration, devices): + with self._lock: self.poll_cycles += 1; self.last_poll_time = datetime.now(); self.last_poll_duration = duration; self.devices_discovered = devices + def record_entity(self): + with self._lock: self.entities_discovered += 1 + def record_command(self): + with self._lock: self.commands_executed += 1; self.messages_received += 1 + def record_error(self, error): + with self._lock: self.errors += 1; self.last_error = f"{datetime.now().isoformat()}: {error}" + def to_dict(self): + with self._lock: + uptime = datetime.now() - self.start_time + return {"status": "running", "uptime_seconds": int(uptime.total_seconds()), "uptime_human": str(uptime).split('.')[0], "start_time": self.start_time.isoformat(), "poll_cycles": self.poll_cycles, "last_poll_time": self.last_poll_time.isoformat() if self.last_poll_time else None, "last_poll_duration_ms": round(self.last_poll_duration * 1000, 1), "devices_discovered": self.devices_discovered, "entities_discovered": self.entities_discovered, "messages_received": self.messages_received, "commands_executed": self.commands_executed, "errors": self.errors, "last_error": self.last_error} +STATS = BridgeStats() + +def setup_logging(config): + level = getattr(logging, config.get("level", "INFO").upper(), logging.INFO) + handlers = [logging.StreamHandler()] + if config.get("file"): handlers.append(logging.FileHandler(config["file"])) + logging.basicConfig(level=level, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=handlers) + return logging.getLogger("ibsystem2mqtt") + +class FriendlyNames: + def __init__(self, config): self.names = config.get("friendly_names", {}) + def get_device_name(self, rs, dev_id): return self.names.get(f"rs{rs}_id{dev_id}", f"IBSystem RS{rs} ID{dev_id}") + def get_entity_name(self, rs, dev_id, path): + safe_path = re.sub(r"[^a-zA-Z0-9_]+", "_", path) + if f"rs{rs}_id{dev_id}_{safe_path}" in self.names: return self.names[f"rs{rs}_id{dev_id}_{safe_path}"] + device_name = self.names.get(f"rs{rs}_id{dev_id}") + readable = self._path_to_readable(path) + return f"{device_name} {readable}" if device_name else f"RS{rs} ID{dev_id} {readable}" + def _path_to_readable(self, path): + m = re.match(r"output\.do\.(\d+)$", path) + if m: return f"Wyjscie DO {m.group(1)}" + m = re.match(r"output\.px\.(\d+)$", path) + if m: return f"PWM {m.group(1)}" + m = re.match(r"input\.di\.(\d+)\.(a|b)$", path) + if m: return f"Wejscie DI {m.group(1)} {m.group(2).upper()}" + m = re.match(r"input\.t\.(\d+)\.value$", path) + if m: return f"Temperatura {m.group(1)}" + return path.replace(".", " ").title() + +@dataclass +class DeviceRecord: + rs: str + id: str + path: str + val: str + @property + def unique_id(self): return f"ibsystem_rs{self.rs}_id{self.id}_{re.sub(r'[^a-zA-Z0-9_]+', '_', self.path)}" + @property + def device_id(self): return f"ibsystem_rs{self.rs}_id{self.id}" + @property + def component(self): + if self.path.startswith("output.do."): return "switch" + if self.path.startswith("input.di."): return "binary_sensor" + return "sensor" + @property + def device_class(self): return "temperature" if "temp" in self.path.lower() or self.path.startswith("input.t.") else None + @property + def unit_of_measurement(self): return "C" if self.path.startswith("input.t.") and "value" in self.path else None + +class DiagnosticsHandler(BaseHTTPRequestHandler): + bridge = None + def log_message(self, format, *args): pass + def do_GET(self): + if self.path in ("/", "/status"): self._send_json(STATS.to_dict()) + elif self.path == "/health": self._send_json({"status": "ok"}) + elif self.path == "/config" and self.bridge: + cfg = json.loads(json.dumps(self.bridge.config)); cfg["mqtt"]["password"] = "***"; self._send_json(cfg) + elif self.path == "/entities" and self.bridge: + self._send_json({"count": len(self.bridge.discovered), "entities": sorted(list(self.bridge.discovered))}) + else: self.send_error(404) + def _send_json(self, data): + content = json.dumps(data, indent=2).encode() + self.send_response(200); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", len(content)); self.end_headers(); self.wfile.write(content) + +class DiagnosticsServer: + def __init__(self, host, port, logger): self.host = host; self.port = port; self.logger = logger; self.server = None + def start(self, bridge): + DiagnosticsHandler.bridge = bridge + try: + self.server = HTTPServer((self.host, self.port), DiagnosticsHandler) + threading.Thread(target=self.server.serve_forever, daemon=True).start() + self.logger.info(f"Diagnostics: http://{self.host}:{self.port}") + except Exception as e: self.logger.error(f"Diagnostics failed: {e}") + def stop(self): + if self.server: self.server.shutdown() + +class IBLSRunner: + def __init__(self, host, port, timeout_ms, logger): self.host = host; self.port = port; self.timeout_ms = timeout_ms; self.logger = logger; self._lock = threading.Lock() + def run(self, command): + cmd = f'/ibsystem/ibls -a {self.host} --pretty -p {self.port} --timeout={self.timeout_ms} -c "{command}"' + try: + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30) + if result.returncode != 0: self.logger.warning(f"ibls failed: {result.stderr}"); STATS.record_error(f"ibls: {result.stderr[:50]}"); return None + return result.stdout.strip() + except Exception as e: self.logger.error(f"ibls error: {e}"); STATS.record_error(str(e)); return None + def get_device(self, rs, dev_id, full=False): + cmd = f"get(rs.{rs}.id.{dev_id};);" if full else f"get(rs.{rs}.id.{dev_id}.input;);get(rs.{rs}.id.{dev_id}.output;);" + output = self.run(cmd) + if not output: return [] + records = [] + for line in output.splitlines(): + m = re.match(r"rs\.(\d+)\.id\.(\d+)\.(.+?)\s*=\s*(.+)", line.strip()) + if m: records.append(DeviceRecord(rs=m.group(1), id=m.group(2), path=m.group(3).strip(), val=m.group(4).strip())) + return records + def set_output(self, rs, dev_id, output_path, value): + with self._lock: return self.run(f"set(rs.{rs}.id.{dev_id}.{output_path}={value};);") is not None + +class MQTTBridge: + def __init__(self, config, ibls, logger): + self.config = config; self.ibls = ibls; self.logger = logger + self.friendly_names = FriendlyNames(config) + self.mqtt_prefix = config["mqtt"]["prefix"]; self.ha_prefix = config["mqtt"]["ha_prefix"] + self.clients = []; self.discovered = set(); self.last_published = {} + self._cmd_lock = threading.Lock(); self._last_cmd = {} + def start(self): + for broker in self.config["mqtt"]["brokers"]: + client = mqtt.Client(client_id=f"ibsystem2mqtt-{broker['name']}", protocol=mqtt.MQTTv311) + client.username_pw_set(self.config["mqtt"]["user"], self.config["mqtt"]["password"]) + client.on_message = self._on_message; client._broker_name = broker["name"] + client.will_set(f"{self.mqtt_prefix}/bridge/availability", "offline", retain=True) + try: + client.connect(broker["host"], broker["port"], 60); client.loop_start(); self.clients.append(client) + self.logger.info(f"Connected: {broker['name']} ({broker['host']})") + except Exception as e: self.logger.error(f"MQTT failed {broker['name']}: {e}"); STATS.record_error(f"MQTT: {broker['name']}") + self._publish_all(f"{self.mqtt_prefix}/bridge/availability", "online") + def stop(self): + self._publish_all(f"{self.mqtt_prefix}/bridge/availability", "offline") + for c in self.clients: c.loop_stop(); c.disconnect() + def _on_message(self, client, userdata, msg): + try: + STATS.record_command(); parts = msg.topic.split("/") + if len(parts) < 5 or parts[0] != self.mqtt_prefix or parts[3] != "set": return + rs, dev_id, target = parts[1].replace("rs",""), parts[2].replace("id",""), parts[4] + m = re.match(r"do(\d+)$", target) + if not m: return + do_num = m.group(1); payload = msg.payload.decode().strip().upper() + value = "1" if payload in ("ON","1","TRUE") else "0" if payload in ("OFF","0","FALSE") else None + if not value: return + key = (rs, dev_id, do_num, value); now = time.time() + with self._cmd_lock: + self._last_cmd = {k:v for k,v in self._last_cmd.items() if v > now-0.5} + if key in self._last_cmd: return + self._last_cmd[key] = now + self.logger.info(f"Command: rs{rs}/id{dev_id}/do{do_num} = {value}") + if self.ibls.set_output(int(rs), int(dev_id), f"setting.light.{do_num}", value): + self._publish_all(f"{self.mqtt_prefix}/rs{rs}/id{dev_id}/output/do/{do_num}/state", "ON" if value=="1" else "OFF") + except Exception as e: self.logger.error(f"Message error: {e}"); STATS.record_error(str(e)) + def poll_and_publish(self): + cfg = self.config["ibsystem"]; rs = cfg["rs"]; full_ids = set(cfg.get("full_ids", [])) + all_records = []; devices = 0 + with ThreadPoolExecutor(max_workers=self.config["polling"].get("parallel_workers", 4)) as ex: + futures = {ex.submit(self.ibls.get_device, rs, i, i in full_ids): i for i in range(1, cfg["max_ids"]+1)} + for f in as_completed(futures): + try: + recs = f.result() + if recs: devices += 1; all_records.extend(recs) + except: pass + for rec in all_records: self._publish_discovery(rec); self._publish_state(rec) + return devices + def _publish_discovery(self, rec): + if rec.unique_id in self.discovered: return + component = rec.component; state_topic = self._state_topic(rec) + payload = {"name": self.friendly_names.get_entity_name(rec.rs, rec.id, rec.path), "state_topic": state_topic, "unique_id": rec.unique_id, "availability_topic": f"{self.mqtt_prefix}/bridge/availability", "device": {"identifiers": [rec.device_id], "name": self.friendly_names.get_device_name(rec.rs, rec.id), "manufacturer": "IBSystem"}} + if component == "switch": + m = re.match(r"output\.do\.(\d+)$", rec.path) + if m: payload.update({"command_topic": f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/set/do{m.group(1)}", "payload_on": "ON", "payload_off": "OFF", "state_on": "ON", "state_off": "OFF"}) + elif component == "binary_sensor": payload.update({"payload_on": "1", "payload_off": "0"}) + elif rec.device_class: payload["device_class"] = rec.device_class + if rec.unit_of_measurement: payload["unit_of_measurement"] = rec.unit_of_measurement; payload["value_template"] = "{{ value | float / 10 }}" if rec.device_class == "temperature" else None + self._publish_all(f"{self.ha_prefix}/{component}/{rec.unique_id}/config", json.dumps(payload)) + self.discovered.add(rec.unique_id); STATS.record_entity() + def _publish_state(self, rec): + topic = self._state_topic(rec) + value = ("ON" if rec.val in ("1","true","ON") else "OFF") if rec.component == "switch" else rec.val + for c in self.clients: + cache = self.last_published.setdefault(c._broker_name, {}) + if cache.get(topic) != value: cache[topic] = value; c.publish(topic, value, retain=True) + def _state_topic(self, rec): + p = rec.path + m = re.match(r"output\.do\.(\d+)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/output/do/{m.group(1)}/state" + m = re.match(r"input\.di\.(\d+)\.(a|b)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/input/di/{m.group(1)}/{m.group(2)}" + m = re.match(r"input\.t\.(\d+)\.(\w+)$", p) + if m: return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/input/t/{m.group(1)}/{m.group(2)}" + return f"{self.mqtt_prefix}/rs{rec.rs}/id{rec.id}/{p.replace('.', '/')}" + def _publish_all(self, topic, payload, retain=True): + for c in self.clients: c.publish(topic, payload, retain=retain) + +def load_config(path=None): + if path and HAS_YAML and Path(path).exists(): + with open(path) as f: user = yaml.safe_load(f) + cfg = json.loads(json.dumps(DEFAULT_CONFIG)) + for k,v in user.items(): cfg[k].update(v) if isinstance(v,dict) and k in cfg else cfg.update({k:v}) + return cfg + return json.loads(json.dumps(DEFAULT_CONFIG)) + +def main(): + import argparse + p = argparse.ArgumentParser(); p.add_argument("-c", "--config"); p.add_argument("-v", "--verbose", action="store_true") + args = p.parse_args(); config = load_config(args.config) + if args.verbose: config["logging"]["level"] = "DEBUG" + logger = setup_logging(config["logging"]); logger.info("Starting IBSystem2MQTT v5") + ibls = IBLSRunner(config["ibsystem"]["host"], config["ibsystem"]["port"], config["ibsystem"]["timeout_ms"], logger) + bridge = MQTTBridge(config, ibls, logger) + diag = DiagnosticsServer(config["http"]["host"], config["http"]["port"], logger) if config.get("http",{}).get("enabled") else None + if diag: diag.start(bridge) + def shutdown(sig, frame): logger.info("Shutdown"); diag and diag.stop(); bridge.stop(); sys.exit(0) + signal.signal(signal.SIGINT, shutdown); signal.signal(signal.SIGTERM, shutdown) + bridge.start() + for c in bridge.clients: c.subscribe(f"{bridge.mqtt_prefix}/+/+/set/#") + interval = config["polling"]["interval_sec"] + while True: + try: + t0 = time.time(); devices = bridge.poll_and_publish(); elapsed = time.time() - t0 + STATS.record_poll(elapsed, devices); logger.debug(f"Poll: {devices} in {elapsed:.2f}s") + time.sleep(max(0, interval - elapsed)) + except Exception as e: logger.error(f"Loop error: {e}"); STATS.record_error(str(e)); time.sleep(interval) + +if __name__ == "__main__": main() diff --git a/ibsystem/ibsystem2mqtt_watchdog.sh b/ibsystem/ibsystem2mqtt_watchdog.sh new file mode 100755 index 0000000..f0a88fb --- /dev/null +++ b/ibsystem/ibsystem2mqtt_watchdog.sh @@ -0,0 +1,39 @@ +# Watchdog dla ibsystem2mqtt +HEALTH_URL="http://127.0.0.1:8080/health" +LOG_FILE="/tmp/ibsystem2mqtt_watchdog.log" +MAX_FAILURES=3 +FAILURE_COUNT_FILE="/tmp/ibsystem2mqtt_failures" + +log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"; } + +check_health() { + response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$HEALTH_URL" 2>/dev/null) + [ "$response" = "200" ] +} + +reset_failures() { echo "0" > "$FAILURE_COUNT_FILE"; } + +increment_failures() { + [ -f "$FAILURE_COUNT_FILE" ] && count=$(cat "$FAILURE_COUNT_FILE") || count=0 + count=$((count + 1)) + echo "$count" > "$FAILURE_COUNT_FILE" + echo "$count" +} + +if check_health; then + reset_failures + exit 0 +fi + +failures=$(increment_failures) +log "Health check failed ($failures/$MAX_FAILURES)" + +if [ "$failures" -ge "$MAX_FAILURES" ]; then + log "Max failures reached, restarting" + pid=$(pgrep -f 'ibsystem2mqtt' | head -1) + [ -n "$pid" ] && kill "$pid" 2>/dev/null && sleep 3 + cd /ibsystem + nohup python3 ibsystem2mqtt_v5.py -c config.yaml >> /tmp/ibsystem2mqtt_stdout.log 2>&1 & + log "Restarted (new PID: $!)" + reset_failures +fi