# Fröling S4 Turbo (Lambdatronic S 3200, V 50.04) -> ESP32-S3 N16R8 -> Home Assistant
# NUR LESEND. Es werden keine Werte in die Heizung geschrieben.
#
# Verkabelung (ESP32-S3 <-> MAX3232-Modul):
#   ESP GPIO17 (TX)  ->  Modul RXD
#   ESP GPIO18 (RX)  <-  Modul TXD
#   ESP 3V3          ->  Modul VCC
#   ESP GND          ->  Modul GND
# MAX3232-Modul (Sub-D Buchse) -> 1:1-Kabel -> Kessel COM2 (Stecker)
#
# Im Kesselmenü: Allg. Einst. -> "COM 2 wird als MODBUS Schnittstelle verwendet" = JA
#                MODBUS Protokoll = 1 (RTU)
# Verkabelung am MAX3232-Modul bei dir: TX/RX sind vertauscht verdrahtet (funktioniert so).
# Registeradressen (0-basiert) gelten fuer das aeltere Protokoll von V 50.04 B05.05
# (im Menue gibt es kein "Modbus Protokoll 2014"): Register 3xxxx minus 30001.
# Registerliste und Skalierung nach dem Projekt
# github.com/GyroGearl00se/ESPHome-Froeling-Lambdatronic_3200 und der
# Fröling-Beschreibung "Kommunikationsprotokoll ModBus Lambdatronic 3200 V 50.04".

substitutions:
  name: froeling
  friendly_name: Fröling
  # Modbus-Adresse des Kessels (Menü "MODBUS Adresse", sonst Werkseinstellung).
  # Kommt keine Antwort: 1 und 2 ausprobieren.
  modbus_address: "1"

esphome:
  name: ${name}
  friendly_name: ${friendly_name}

esp32:
  board: esp32-s3-devkitc-1
  flash_size: 16MB
  framework:
    type: esp-idf

# N16R8 = 8 MB Octal-PSRAM
psram:
  mode: octal
  speed: 80MHz

logger:
  level: INFO

api:

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  # Bei Verbindungsproblemen aktivieren:
  # output_power: 8.5dB
  ap:
    ssid: "Froeling Fallback"
    password: "fallback1234"

captive_portal:

uart:
  id: uart_froeling
  tx_pin: GPIO17
  rx_pin: GPIO18
  baud_rate: 57600
  data_bits: 8
  stop_bits: 1
  parity: NONE

modbus:
  id: modbus1
  uart_id: uart_froeling

modbus_controller:
  - id: froeling
    address: ${modbus_address}
    modbus_id: modbus1
    setup_priority: -10
    update_interval: 60s
    command_throttle: 50ms

sensor:
  # --- Kessel ---
  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Kesseltemperatur"
    address: 0
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Abgastemperatur"
    address: 1
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Rücklauftemperatur"
    address: 10
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Restsauerstoff"
    address: 3
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "%"
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Saugzug Drehzahl"
    address: 6
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "rpm"
    state_class: measurement

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Betriebsstunden"
    address: 98
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "h"
    state_class: total_increasing

  # --- Außen ---
  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Außentemperatur"
    address: 4
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  # --- Puffer 1 ---
  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Puffer 1 oben"
    address: 118
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Puffer 1 mitte"
    address: 119
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Puffer 1 unten"
    address: 120
    register_type: read
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  # --- Boiler / Brauchwasser und Heizkreis ---
  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Boiler Temperatur"
    address: 93
    register_type: read
    value_type: S_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  - platform: modbus_controller
    modbus_controller_id: froeling
    name: "Vorlauf Heizkreis 1"
    address: 21
    register_type: read
    value_type: S_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    state_class: measurement
    accuracy_decimals: 1
    filters:
      - multiply: 0.5

  # --- Zustandscodes (intern, daraus entstehen die Texte unten) ---
  - platform: modbus_controller
    modbus_controller_id: froeling
    id: anlagenzustand_code
    name: "Anlagenzustand Code"
    address: 4000
    register_type: read
    value_type: U_WORD
    internal: true

  - platform: modbus_controller
    modbus_controller_id: froeling
    id: kesselzustand_code
    name: "Kesselzustand Code"
    address: 4001
    register_type: read
    value_type: U_WORD
    internal: true

  # --- WLAN-Signal ---
  - platform: wifi_signal
    name: "WLAN Signal"
    update_interval: 60s
    entity_category: diagnostic

text_sensor:
  - platform: template
    name: "Anlagenzustand"
    update_interval: 60s
    lambda: |-
      static const char* const names[] = {
        "Dauerlast", "Brauchwasser", "Automatik", "Scheitholzbetrieb",
        "Reinigen", "Ausgeschaltet", "Extraheizen", "Kaminkehrer", "Reinigen"
      };
      if (!id(anlagenzustand_code).has_state()) return {};
      int v = (int) id(anlagenzustand_code).state;
      if (v >= 0 && v < (int)(sizeof(names) / sizeof(names[0]))) return std::string(names[v]);
      return std::string("----");

  - platform: template
    name: "Kesselzustand"
    update_interval: 60s
    lambda: |-
      static const char* const names[] = {
        "STÖRUNG", "Kessel Aus", "Anheizen", "Heizen", "Feuererhaltung",
        "Feuer Aus", "Tür offen", "Vorbereitung", "Vorwärmen", "Zünden",
        "Abstellen Warten", "Abstellen Warten1", "Abstellen Einschub1",
        "Abstellen Warten2", "Abstellen Einschub2", "Abreinigen", "2h warten",
        "Saugen / Heizen", "Fehlzündung", "Betriebsbereit", "Rost schließen",
        "Stoker leeren", "Vorheizen", "Saugen", "RSE schließen", "RSE öffnen",
        "Rost kippen", "Vorwärmen-Zünden", "Resteinschub", "Stoker auffüllen",
        "Lambdasonde aufheizen", "Gebläsenachlauf I", "Gebläsenachlauf II",
        "Abgestellt", "Nachzünden", "Zünden Warten", "FB: RSE schließen",
        "FB: Kessel belüften", "FB: Zünden", "FB: min. Einschub", "RSE schließen",
        "STÖRUNG: STB/NA", "STÖRUNG: Kipprost", "STÖRUNG: FR-Überdr.",
        "STÖRUNG: Türkont.", "STÖRUNG: Saugzug", "STÖRUNG: Umfeld",
        "FEHLER: STB/NA", "FEHLER: Kipprost", "FEHLER: FR-Überdr.",
        "FEHLER: Türkont.", "FEHLER: Saugzug", "FEHLER: Umfeld", "FEHLER: Stoker",
        "STÖRUNG: Stoker", "FB: Stoker leeren", "Vorbelüften", "STÖRUNG: Hackgut",
        "FEHLER: Hackgut", "NB: Tür offen", "NB: Anheizen", "NB: Heizen",
        "FEHLER: STB/NA", "FEHLER: Allgemein", "NB: Feuer Aus", "Selbsttest aktiv",
        "Fehlerbeh. 20min", "FEHLER: Fallschacht", "STÖRUNG: Fallschacht",
        "Reinigen möglich", "Heizen - Reinigen", "SH Anheizen", "SH Heizen",
        "SH Heiz/Abstell", "STÖRUNG sicher", "AGR Nachlauf", "AGR reinigen",
        "Zündung AUS", "Filter reinigen", "Anheizassistent", "SH Zünden",
        "SH Störung", "Sensorcheck"
      };
      if (!id(kesselzustand_code).has_state()) return {};
      int v = (int) id(kesselzustand_code).state;
      if (v >= 0 && v < (int)(sizeof(names) / sizeof(names[0]))) return std::string(names[v]);
      return std::string("----");

  - platform: wifi_info
    ip_address:
      name: "IP-Adresse"
      entity_category: diagnostic

button:
  - platform: restart
    name: "Neustart"
