So, ich habe das Gerät noch etwas aufgewertet. Da man nach Einbau die Comfort-Cloud nicht mehr braucht, hab ich die Geräte daraus gelöscht und das interne WiFi abgeschaltet. Dann sind im ausgeschalteten Zustand der Klimaanlage aber auch alle LED an der Vorderseite aus, so dass man nicht sieht, ob sie überhaupt Strom hat. Das wollte ich ändern.
Ich habe dann zunächst eine zusätzliche RGB-LED (5mm, WS2811) über ein Kabel und einen freien Kanal des Levelshifters (!!) an die Platine angeschlossen. In die Datenleitung kommt ein 470k Widerstand und ein 47uF-Elko zwischen 5V und GND war ja eh schon da. Diese LED hab ich in der Anlage so befestigt, dass Sie aus dem oben offenen Gehäuse raus an die Decke strahlt.
Dann hab ich den Code erweitert und Folgendes implementiert:
- wenn kein WiFi: rot blinken
- wenn WiFi aber Timeout zur AC: magenta blinken
- wenn WiFi und Verbindung zur AC aber keine Verbindung zur HA-API: gelb blinken
- wenn WiFi und Verbindung zur AC und HA-API: WiFi RSSI als RGB-Farbe (grün bis rot)
Um die Helligkeit der LED einstellen zu können, hab ich entsprechende Regler eingebaut. Ich hole mir im YAML außerdem die Außenhelligkeit von meiner Wetterstation, da kann aber auch jeder andere Helligkeitssensor eingetragen werden. Liegt die Außenhelligkeit über dem einstellbaren Threshold, leuchtet die LED mit der eingestellten “Brightness hoch”, ansonsten mit der “Brightness niedrig”. Das Blinken ist immer mit Helligkeit 100%.
Zusätzlich hab ich implementiert, dass der ESP - wenn einige Zeit die WiFi-Verbindung unter -80dBm liegt - ein neuer WiFi-Scan initiiert wird. Das ist bei mir relevant, weil mehrere Access-Points habe und der ESP mir ansonsten zu lange an “schlechten” AP klebt, wenn er sich einmal damit verbunden hat.
Zuletzt hab ich dann einen Schalter implementiert, über den der ESP aus HA heraus neu gestartet werden kann, sowie einige Diagnose-Sensoren für die Anzeige in HA (IP, Mac-Adresse, BSSID, WiFi-Qualität und Info bzgl. der Kommunikation mit der Klimaanlage).
Falls wer die Konfiguration so verwendet, bitte beachten… der ESP holt sich nach dem Start die Zeit per SNTP. Sobald die Verbindung zur Klima steht, wird der Zeitstempel in HA angezeigt. Nach Unterbrechung der Verbindung - warum auch immer - wird der Zeitstempel erneuert. Nach dem ersten Start nach Firmware-Flashen hat der ESP aber noch keine gültige Zeit und der Zeitstempel zeigt “Unbekannt”. Dann einmal aus HA heraus den ESP neu starten und ab dann passt das alles bis zum nächsten Flashen.
# 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, Mac-Adresse, BSSID, WiFi-Qualität und Info bzgl. der Kommunikation mit der Klimaanlage)
substitutions:
const_led_brightness_low: 30 # low-Helligkeit Initialwert WiFi-Qualitäts-LED für Number-Control, ohne %
const_led_brightness_high: 100 # high-Helligkeit Initialwert WiFi-Qualitäts-LED für Number-Control, ohne %
const_outside_brightness_threshold: 4800 # Helligkeitsschwelle für Brightness WiFi-Qualitäts-LED
const_ac_timeout: '20000'
esphome:
name: ac-schlafzimmer
friendly_name: AC-Schlafzimmer
project:
name: "michael.ac-controller"
version: "3.3.0"
on_boot:
then:
- text_sensor.template.publish:
id: ac_verbunden_seit
state: "Niemals"
esp32:
board: esp32-c6-devkitc-1
framework:
type: esp-idf
# Logging ein
logger:
# Home Assistant API ein
api:
encryption:
key: "blabla"
ota:
- platform: esphome
password: "blabla"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
enable_btm: True
enable_rrm: True
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Ac-Schlafzimmer Fallback Hotspot"
password: "blabla"
captive_portal:
#
# Konfiguration externe Komponente Panasonic-AC #################################################
#
# 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
# 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: 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: sntp
id: sntp_time
timezone: Europe/Berlin
servers:
- 0.europe.pool.ntp.org
- 1.europe.pool.ntp.org
- 0.pool.ntp.org
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
on_value:
then:
- component.update: wifi_signal_prozent
- if:
# prüfen, ob RSSI eine zeitlang unter -80 dBm, dann Roaming/Reconnect versuchen.
condition:
- lambda: 'return x < -80;'
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));
# Helligkeit vom Umweltsensor aus Home Assistant
# verwendet für Steuerung der Brightness der LED
- platform: homeassistant
name: "Helligkeit Außen"
id: helligkeit_aussen
entity_id: sensor.gw1100a_solar_lux
text_sensor:
# Infos für HA-Anzeige
- platform: wifi_info
ip_address:
name: IP Addresse
entity_category: "diagnostic"
mac_address:
name: Mac Addresse
entity_category: "diagnostic"
bssid:
name: BSSID
entity_category: "diagnostic"
- platform: template
name: "AC verbunden seit"
id: ac_verbunden_seit
entity_category: "diagnostic"
icon: mdi:clock-check-outline
update_interval: never
binary_sensor:
# Überwachung der Schnittstelle zur AC
- platform: template
name: "AC Verbindung"
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_verbunden_seit
state: "Getrennt"
number:
# erzeugt ein Control, über den der Schwellwert der Brightness-Regelung angepasst werden kann
- platform: template
name: "LED Threshold"
min_value: 0
max_value: 7000
step: 50
initial_value: ${const_outside_brightness_threshold}
id: brightness_threshold_var
optimistic: true
set_action:
- lambda: id(brightness_threshold_var).publish_state(x);
# erzeugt ein Control, über den die niedrige Brightness angepasst werden kann
- platform: template
name: "LED Brightness niedrig"
id: brightness_low_var
min_value: 20
max_value: 100
step: 1
initial_value: ${const_led_brightness_low}
set_action:
- lambda: id(brightness_low_var).publish_state(x);
# erzeugt ein Control, über den die höhere Brightness angepasst werden kann
- platform: template
name: "LED Brightness hoch"
id: brightness_high_var
min_value: 20
max_value: 100
step: 1
initial_value: ${const_led_brightness_high}
set_action:
- lambda: id(brightness_high_var).publish_state(x);
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 -90 bis -55 dBm:
# -90 dBm = HSV(355°, 100%, 100%) ≈ RGB(255, 0, 42) — Reines Rot (sehr schlechte Verbindung)
# -85 dBm = HSV(326°, 100%, 100%) ≈ RGB(255, 0, 182) — Magenta
# -80 dBm = HSV(297°, 100%, 100%) ≈ RGB(204, 0, 255) — Violett
# -75 dBm = HSV(268°, 100%, 100%) ≈ RGB(14, 0, 255) — Blau
# -70 dBm = HSV(239°, 100%, 100%) ≈ RGB(0, 142, 255) — Hellblau
# -65 dBm = HSV(210°, 100%, 100%) ≈ RGB(0, 255, 255) — Cyan
# -60 dBm = HSV(180°, 100%, 100%) ≈ RGB(0, 255, 0) — Grün
# -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 < -90) dbm = -90;
// Interpolationswert: -90 dBm = 0.0 → -55 dBm = 1.0
float t = (dbm + 90.0) / 35.0;
// Farbton von 355° (rot) bis 140° (grün-türkis)
float 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
if (id(helligkeit_aussen).state < id(brightness_threshold_var).state) {
brightness = id(brightness_low_var).state / 100;
} else {
brightness = id(brightness_high_var).state / 100;
}
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);
# 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;
- 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;
- 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;
- 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:
- lambda: |-
char buffer[25];
auto now = id(sntp_time).now();
if (now.is_valid()) {
now.strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S");
ESP_LOGI("Klimaanlage", "AC connected since: %s", buffer);
id(ac_verbunden_seit).publish_state(buffer);
} else {
id(ac_verbunden_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
Anschlussleiste zur Klima (Pin oben = 1, Pin unten = 4):
- Pin1: ESP TX
- Pin2: ESP RX
- Pin3: 5V
- Pin4: GND
Anschlussleiste zur LED (Pin oben = 1, Pin unten = 3):
- Pin1: GND
- Pin2: 5V
- Pin3: Data
Hier noch die Ansichten aus HA dazu:
by HarryP: Zusammenführung Doppelpost (bei Änderungen oder hinzufügen von Inhalten bitte die „Bearbeitungsfunktion“ anstatt „Antworten“ zu nutzen)