@Ralftopas Ich glaube, das hab ich falsch formuliert. WS2811 ist der Chip, wenn der in einer LED verbaut ist wird es WS2812. Da findet man reichlich 5mm LED auf den bekannten Plattformen. Ich hab den Dout-Pin abgeknipst, muss ja nicht in Reihe geschaltet werden.
Hier noch mein aktueller Code, läuft so auf meinen beiden Anlagen bislang stabil ohne Ausfälle. Ich hab feste IP vergeben, geht bestimmt auch anders. Ich hab diesen Code in einer “ac-controller-main-config.yaml”. Das konkrete Gerät dann wie in der zweiten Code-Section.
# Lokale Steuerung der Panasonic Klimaanlage
#
# basiert auf github://DomiStyle/esphome-panasonic-ac
#
# Mein Modell: CS-Z20XKEW mit 5-pol. Anschluss (PAP-05V-S) an Modul CZ-TACG1
# Board Waveshare ESP32-C6-Mini
# zusätzlich Status LED onboard und extern
# zusätzlich Fern-Restart per HA
# zusätzlich einige Diagnosedaten (IP, BSSID, WiFi-Qualität und Info bzgl. der Kommunikation mit der Klimaanlage)
# In der Geräte-Konfigurations-YAML benötigte substitutions:
# device_name: wasauchimmer
# friendly_name: wasauchimmer
# const_ac_timeout: '20000' # Timout in ms bis Verbindungsverlust zur AC signalisiert wird
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
project:
name: "Michael.AC-Controller"
version: "4.0.8"
on_boot:
then:
- text_sensor.template.publish:
id: ac_link_seit
state: "Niemals"
- wait_until:
condition:
time.has_time:
timeout: 15s
- if:
condition:
time.has_time:
then:
- lambda: |-
char buffer[25];
auto now = id(systemtime).now();
now.strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S");
ESP_LOGI("Klimaanlage", "Boot-time: %s", buffer);
id(systemstart).publish_state(buffer);
else:
- lambda: |-
ESP_LOGE("Klimaanlage", "No valid time, setting boot-timestamp to unkown.");
id(systemstart).publish_state("Unbekannt");
esp32:
board: esp32-c6-devkitc-1
framework:
type: esp-idf
# Logging ein
logger:
# Home Assistant API ein
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: ${wifi_ip}
gateway: 192.168.30.1
subnet: 255.255.255.0
enable_btm: True
enable_rrm: True
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${device_name} Fallback Hotspot"
password: !secret fallback_password
captive_portal:
#
# Konfiguration externe Komponente Panasonic-AC und ADC für Lichtsensor ###########################
#
# Requires ESPHome 1.15.0+ for the even parity option
uart:
tx_pin: GPIO4 # weiß, HV2
rx_pin: GPIO5 # gelb, HV1
baud_rate: 9600
parity: EVEN
external_components:
- source: github://DomiStyle/esphome-panasonic-ac
components: [panasonic_ac]
climate:
# Klima-Komponente für HA
- platform: panasonic_ac
id: my_panasonic_ac
# For CZ-TACG1
type: cnt
name: Panasonic AC
horizontal_swing_select:
name: Panasonic AC Horizontal Swing Mode
vertical_swing_select:
name: Panasonic AC Vertical Swing Mode
outside_temperature:
name: Panasonic AC Outside Temperature
# Enable as needed
# eco_switch:
# name: Panasonic AC Eco Switch
# econavi_switch:
# name: Panasonic AC Econavi Switch
nanoex_switch:
name: Panasonic AC NanoeX Switch
mild_dry_switch:
name: Panasonic AC Mild Dry Switch
current_power_consumption:
name: Panasonic AC Power Consumption
# Useful when the ac does not report a current temperature (CZ-TACG1 only)
# current_temperature_sensor: temperature_sensor_id
#
# Konfiguration eigene funktionale Erweiterungen ################################################
#
# Schnittstelle überwachen und Zeit updaten, wenn sich Werte ändern
on_state:
then:
- lambda: |-
id(ac_last_seen) = millis();
globals:
- id: ha_connected
type: bool
restore_value: no
initial_value: 'false'
- id: bad_wifi_signal_count
type: int
restore_value: no
initial_value: '0'
- id: led_last_red
type: float
restore_value: no
initial_value: '0'
- id: led_last_green
type: float
restore_value: no
initial_value: '0'
- id: led_last_blue
type: float
restore_value: no
initial_value: '0'
- id: led_last_brightness
type: float
restore_value: no
initial_value: '0'
- id: led_last_state
type: std::string
restore_value: no
initial_value: ''
- id: brightness_sensor_last_value
type: float
restore_value: no
initial_value: '0'
- id: ac_timeout
type: uint32_t
restore_value: no
initial_value: ${const_ac_timeout}
- id: ac_last_seen
type: uint32_t
restore_value: no
initial_value: '0'
- id: ac_connected
type: bool
restore_value: no
initial_value: 'false'
time:
- platform: homeassistant
id: systemtime
switch:
# Remote Restart-Button in HA
- platform: restart
name: "ESP Neustart"
sensor:
# WiFi Signalstärke RSSI in dBm
- platform: wifi_signal
name: "WiFi Signal dBm"
id: wifi_signal_dbm
update_interval: never
entity_category: "diagnostic"
device_class: signal_strength
filters:
- lambda: |-
static float last_sent = NAN; // letzter gesendeter Wert
// Wenn Wert < -80 -> sofort senden
if (x < -80.0) {
last_sent = x;
return x;
}
// Wenn Wert >= -80 und Unterschied >= 3 -> senden
if (isnan(last_sent) || fabs(x - last_sent) >= 3.0) {
last_sent = x;
return x;
}
// sonst nicht senden
return {};
on_value:
then:
- component.update: wifi_signal_prozent
- if:
# prüfen, ob RSSI eine zeitlang unter -85 dBm, dann Roaming/Reconnect versuchen.
condition:
- lambda: 'return x < -85;'
then:
- lambda: 'id(bad_wifi_signal_count)++;'
else:
- lambda: 'id(bad_wifi_signal_count) = 0;'
- if:
condition:
- lambda: 'return id(bad_wifi_signal_count) > 10;'
then:
- lambda: |-
ESP_LOGI("Klimaanlage", "Bad WiFi-connected, starting new scan for better AP");
id(bad_wifi_signal_count) = 0;
wifi::global_wifi_component->start_scanning();
# WiFi Signalstärke RSSI in %
- platform: template
name: "WiFi Signal Prozent"
id: wifi_signal_prozent
icon: mdi:wifi
entity_category: "diagnostic"
unit_of_measurement: "%"
update_interval: never
# übliche Funktion zur Umrechnung von RSSI in Prozent
accuracy_decimals: 0
lambda: |-
float rssi = id(wifi_signal_dbm).state;
return round(min(max(2 * (rssi + 100.0), 0.0), 100.0));
- platform: uptime
name: Laufzeit
# Helligkeitssensor Rohwert [V], LDR mit Spannungsteiler 10k
- platform: adc
pin: GPIO1
id: sensor_helligkeit_raw
attenuation: 12db
update_interval: 500ms
internal: true
samples: 5
filters:
# Gätten der Messwerte und Ausreißer entfernen
- quantile:
window_size: 7
send_every: 4
send_first_at: 3
quantile: .25
- delta: 0.033
on_value:
then:
lambda: |-
ESP_LOGI("Klimaanlage", "Helligkeit Sensor brightness raw value = %.3fV", x);
if (id(brightness_sensor_last_value) != x) {
id(brightness_sensor_last_value) = x;
id(sensor_helligkeit).update();
}
# Für Anzeige in HA, wird per Intervall upgedatet
- platform: template
name: "Sensor Helligkeit"
id: sensor_helligkeit
icon: mdi:lightbulb-question-outline
entity_category: "diagnostic"
unit_of_measurement: "%"
accuracy_decimals: 0 # Keine Nachkommastellen
state_class: measurement
# Die Umrechnung von Volt (0-3.3V) in Prozent (0-100%)
lambda: |-
return (id(sensor_helligkeit_raw).state / 3.3) * 100;
- platform: template
name: "LED Helligkeit"
id: led_helligkeit
icon: mdi:lightbulb-on-outline
entity_category: "diagnostic"
unit_of_measurement: "%"
accuracy_decimals: 0 # Keine Nachkommastellen
state_class: measurement
text_sensor:
# Infos für HA-Anzeige
- platform: wifi_info
ip_address:
name: "IP Addresse"
entity_category: "diagnostic"
bssid:
name: "BSSID"
entity_category: "diagnostic"
- platform: template
name: "AC-Link seit"
id: ac_link_seit
entity_category: "diagnostic"
icon: mdi:clock-check-outline
update_interval: never
- platform: template
name: "Systemstart"
id: systemstart
entity_category: "diagnostic"
icon: mdi:clock-check
update_interval: never
- platform: template
name: "LED Mode"
id: led_mode
entity_category: "diagnostic"
icon: mdi:led-variant-outline
update_interval: never
binary_sensor:
# Überwachung der Schnittstelle zur AC
- platform: template
name: "AC-Link"
id: ac_verbindung
entity_category: "diagnostic"
device_class: connectivity
lambda: |-
if (id(ac_last_seen) == 0) return false; // noch nie aktualisiert → nicht verbunden
// sonst auswerten, wenn innerhalb der timeout-Zeit in ms eine Antwort kam
id(ac_connected) = (millis() - id(ac_last_seen)) < id(ac_timeout);
return id(ac_connected);
# Textsensor mit Zeitstempel befüllen
on_press:
then:
- script.execute: set_ac_connection_on_time
on_release:
then:
- text_sensor.template.publish:
id: ac_link_seit
state: "Getrennt"
number:
# erzeugt Controls, über den die LED Brightness in Abhängigkeit des Helligkeitssensors angepasst werden kann
- platform: template
name: "LED max"
id: led_brightness_max_slider_value
icon: mdi:lightbulb-on-outline
min_value: 0
max_value: 100
step: 1
initial_value: 100
restore_value: true
optimistic: true
set_action:
- lambda: |-
if (x < id(led_brightness_min_slider_value).state) {
id(led_brightness_min_slider_value).publish_state(x);
}
id(led_brightness_max_slider_value).publish_state(x);
- platform: template
name: "LED min"
id: led_brightness_min_slider_value
icon: mdi:lightbulb-night-outline
min_value: 0
max_value: 100
step: 1
initial_value: 20
restore_value: true
optimistic: true
set_action:
- lambda: |-
if (x > id(led_brightness_max_slider_value).state) {
id(led_brightness_max_slider_value).publish_state(x);
}
id(led_brightness_min_slider_value).publish_state(x);
- platform: template
name: "LED Gain"
id: led_brightness_gain_slider_value
icon: mdi:trending-up
min_value: 0
max_value: 1
step: 0.1
initial_value: 0
restore_value: true
optimistic: true
light:
# Konfiguration ESP32 C6 Onboard-LED
- platform: esp32_rmt_led_strip
id: status_led
chipset: WS2811
pin: GPIO0
num_leds: 1
rmt_symbols: 48
rgb_order: RGB
default_transition_length: 700ms
internal: true
effects:
- pulse:
name: led_pulse
transition_length:
on_length: 500ms
off_length: 200ms
update_interval: 500ms
min_brightness: 0%
max_brightness: 100%
# Konfiguration ESP32 C6 extrene WS2812 LED
- platform: esp32_rmt_led_strip
id: onboard_led
chipset: WS2812
pin: GPIO8
num_leds: 1
rmt_symbols: 48
rgb_order: RGB
default_transition_length: 700ms
internal: true
effects:
- pulse:
name: led_pulse
transition_length:
on_length: 500ms
off_length: 200ms
update_interval: 500ms
min_brightness: 0%
max_brightness: 100%
script:
# WiFi-Signalstärke linear in RGB-Farben abbilden
- id: set_wifi_quality_led
mode: single
then:
- if:
condition:
- api.connected:
then:
# Farbverlauf bei RSSI von -80 bis -55 dBm:
# -80 dBm = HSV(355°, 100%, 100%) ≈ RGB(255, 0, 42) — Reines Rot (sehr schlechte Verbindung)
# -55 dBm = HSV(140°, 100%, 100%) ≈ RGB(0, 255, 150) — Türkisgrün (sehr gute Verbindung)
- lambda: |-
int dbm = (int) id(wifi_signal_dbm).state;
if (dbm > -55) dbm = -55;
if (dbm < -85) dbm = -85;
float hue;
if (dbm <= -85) {
hue = 355.0; // festes Rot
} else {
// Interpolationswert: -85 dBm = 0.0 → -55 dBm = 1.0
float t = (dbm + 85.0) / 30.0;
hue = 355.0 - t * (355.0 - 140.0);
}
// HSV zu RGB konvertieren
float s = 1.0;
float v = 1.0;
float c = v * s;
float h_prime = hue / 60.0;
float x = c * (1.0 - fabs(fmod(h_prime, 2.0) - 1.0));
float m = v - c;
float r = 0, g = 0, b = 0, brightness = 0;
if (0 <= h_prime && h_prime < 1) { r = c; g = x; b = 0; }
else if (1 <= h_prime && h_prime < 2) { r = x; g = c; b = 0; }
else if (2 <= h_prime && h_prime < 3) { r = 0; g = c; b = x; }
else if (3 <= h_prime && h_prime < 4) { r = 0; g = x; b = c; }
else if (4 <= h_prime && h_prime < 5) { r = x; g = 0; b = c; }
else if (5 <= h_prime && h_prime <= 6) { r = c; g = 0; b = x; }
// Addiere m zur Normalisierung auf [0–1]
r += m;
g += m;
b += m;
// Brightness bestimmen in Abhängigkeit Umgebungslicht
float slider_value_max = id(led_brightness_max_slider_value).state; // Slider in HA von 0 - 100
float slider_value_min = id(led_brightness_min_slider_value).state; // Slider in HA von 0 - 100
// Sensor auslesen, Vertsärkungsfaktor anwenden und auf 3.3 = Maximalspannung begrenzen
float adc_value = id(sensor_helligkeit_raw).state;
adc_value = std::min(adc_value + adc_value * id(led_brightness_gain_slider_value).state, 3.3f);
adc_value += adc_value * (id(led_brightness_gain_slider_value).state);
// 1. Lineare Interpolation (als Basis) von 22% bis Slider-Wert
float slope = (slider_value_max - slider_value_min) / 3.3;
float linear_brightness_percent = slider_value_min + (adc_value * slope);
// 2. Wert für logarithmische Kurve normalisieren (0.0 bis 1.0)
float normalized = (linear_brightness_percent - slider_value_min) / (slider_value_max - slider_value_min);
// 3. Logarithmische Kurve erzeugen
// Der normalisierte Wert wird mit dem Exponenten x versehen.
// Ein kleinerer Exponent (z. B. 0.2) macht die Kurve "aggressiver", d.h. kleine Helligkeitsveränderungen haben eine stärkere Auswirkung.
// Ein größerer Exponent (z. B. 0.6) macht die Kurve sanfter.
float nonlinear_value = pow(normalized, 0.6);
// 4. Wert zurück auf den gewünschten Bereich skalieren
float final_brightness_percent = slider_value_min + nonlinear_value * (slider_value_max - slider_value_min);
// Farbhelligkeit korrigieren: dunkle Töne wie Blau/Türkis etwas anheben
// Bereich: Hue 180°–260° (Blau/Blaugrün)
float correction_factor = 1.0f;
if (hue >= 180.0f && hue <= 260.0f) {
correction_factor = 1.15f; // +15% Helligkeit
}
// Korrektur anwenden und auf Maximalwert 1.0 begrenzen
final_brightness_percent = final_brightness_percent * correction_factor;
// 5. Clamping und auf den gültigen Bereich skalieren
brightness = std::max((slider_value_min / 100.0), std::min(1.0, final_brightness_percent / 100.0));
// und nach HA schicken
id(led_helligkeit).publish_state(brightness * 100.0f);
ESP_LOGI("Klimaanlage",
"WiFi-RSSI %ddBm, RGB(%.0f%%, %.0f%%, %.0f%%), Brightness %.0f%%; ", dbm, r * 100.0f, g * 100.0f, b * 100.0f, brightness * 100.0f);
id(led_set_color).execute(r, g, b, brightness);
id(led_mode).publish_state("WiFi-Status");
# Pulsierende LED in rot (WiFi-loss), immer Brightness = 100%
- id: led_pulse_red
then:
- lambda: |-
// RGB-Werte in den globalen Variablen für die WiFi-Qualität zurücksetzen
id(led_last_red) = 0;
id(led_last_green) = 0;
id(led_last_blue) = 0;
// Wenn Pulse hat LED immer 100% Helligkeit
id(led_helligkeit).publish_state(100.0f);
id(led_mode).publish_state("WiFi-loss");
- light.turn_on:
id: status_led
red: 100%
green: 0%
blue: 0%
effect: led_pulse
- light.turn_on:
id: onboard_led
red: 100%
green: 0%
blue: 0%
effect: led_pulse
# Pulsierende LED in gelb (HA API-loss), immer Brightness = 100%
- id: led_pulse_yellow
then:
- lambda: |-
// RGB-Werte in den globalen Variablen für die WiFi-Qualität zurücksetzen
id(led_last_red) = 0;
id(led_last_green) = 0;
id(led_last_blue) = 0;
// Wenn Pulse hat LED immer 100% Helligkeit
id(led_helligkeit).publish_state(100.0f);
id(led_mode).publish_state("API-loss");
- light.turn_on:
id: status_led
red: 95%
green: 100%
blue: 0%
effect: led_pulse
- light.turn_on:
id: onboard_led
red: 95%
green: 100%
blue: 0%
effect: led_pulse
# Pulsierende LED in magenta (AC communication loss), immer Brightness = 100%
- id: led_pulse_magenta
then:
- lambda: |-
// RGB-Werte in den globalen Variablen für die WiFi-Qualität zurücksetzen
id(led_last_red) = 0;
id(led_last_green) = 0;
id(led_last_blue) = 0;
// Wenn Pulse hat LED immer 100% Helligkeit
id(led_helligkeit).publish_state(100.0f);
id(led_mode).publish_state("AC-loss");
- light.turn_on:
id: status_led
red: 83%
green: 0%
blue: 100%
effect: led_pulse
- light.turn_on:
id: onboard_led
red: 83%
green: 0%
blue: 100%
effect: led_pulse
# LED bei Farbänderung ansteuern, Parameter = RGB in %
- id: led_set_color
parameters:
red: float
green: float
blue: float
brightness: float
then:
- if:
condition:
- lambda: 'return id(led_last_red) != red || id(led_last_green) != green || id(led_last_blue) != blue || id(led_last_brightness) != brightness;'
then:
- lambda: |-
// Speichere die neuen Werte in den globalen Variablen
id(led_last_red) = red;
id(led_last_green) = green;
id(led_last_blue) = blue;
id(led_last_brightness) = brightness;
- light.turn_on:
id: status_led
red: !lambda 'return red;'
green: !lambda 'return green;'
blue: !lambda 'return blue;'
brightness: !lambda 'return brightness;'
effect: none
- light.turn_on:
id: onboard_led
red: !lambda 'return red;'
green: !lambda 'return green;'
blue: !lambda 'return blue;'
brightness: !lambda 'return brightness;'
effect: none
# Zeit speichern, wann AC online ging seit letzter Unterbrechung
- id: set_ac_connection_on_time
then:
- wait_until:
condition:
time.has_time:
timeout: 15s
- if:
condition:
time.has_time:
then:
- lambda: |-
char buffer[25];
auto now = id(systemtime).now();
now.strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S");
ESP_LOGI("Klimaanlage", "AC connected since: %s", buffer);
id(ac_link_seit).publish_state(buffer);
else:
- lambda: |-
ESP_LOGI("Klimaanlage", "No valid time, setting ac-connection-timestamp to unkown.");
id(ac_link_seit).publish_state("Unbekannt");
interval:
# Steuerung der LED:
# wenn kein WiFi: rot blinken
# wenn WiFi aber Timeout zur AC: magenta blinken
# wenn WiFi und Verbindung zur AC aber keine Verbindung zur API: gelb blinken
# wenn WiFi und Verbindung zur AC und API: RSSI als RGB-Farbe
- interval: 2s
then:
- if:
condition:
wifi.connected: {}
then:
- component.update: wifi_signal_dbm
- if:
condition:
not:
- lambda: 'return id(ac_connected);'
then:
- if:
condition:
- lambda: 'return id(led_last_state) != "led_pulse_magenta";'
then:
- lambda: 'id(led_last_state) = "led_pulse_magenta";'
- script.execute: led_pulse_magenta
else:
- if:
condition:
not:
api.connected:
then:
- if:
condition:
- lambda: 'return id(led_last_state) != "led_pulse_yellow";'
then:
- lambda: 'id(led_last_state) = "led_pulse_yellow";'
- script.execute: led_pulse_yellow
else:
- lambda: 'id(led_last_state) = "";'
- delay: 200ms
- script.execute: set_wifi_quality_led
else:
- script.execute: led_pulse_red
Hier dann das konkrete Gerät, da wird der obige Block eingebunden. Wie gesagt, geht bestimmt auch anders, ich mach es so…
# Lokale Steuerung der Panasonic Klimaanlage
#
# basiert auf github://DomiStyle/esphome-panasonic-ac
#
# Mein Modell: CS-Z20XKEW mit 5-pol. Anschluss (PAP-05V-S) an Modul CZ-TACG1
# Board Waveshare ESP32-C6-Mini
# zusätzlich Status LED onboard und extern
# zusätzlich Fern-Restart per HA
# zusätzlich einige Diagnosedaten (IP, BSSID, WiFi-Qualität und Info bzgl. der Kommunikation mit der Klimaanlage)
substitutions:
# Gerätespezifische Namen
device_name: ac-schlafzimmer
friendly_name: AC-Schlafzimmer
# IP
wifi_ip: 192.168.30.133
# Timout in ms bis Verbindungsverlust zur AC signalisiert wird
const_ac_timeout: '20000'
# Hauptkonfiguration wird hier eingebunden
<<: !include ac-controller-main-config.yaml