diff --git a/common/status_led.yaml b/common/status_led.yaml new file mode 100644 index 0000000..d0b6085 --- /dev/null +++ b/common/status_led.yaml @@ -0,0 +1,126 @@ +--- +# Status on a visible RGB light, which stays a normal light otherwise: +# network down red, slow blink +# no API client (HA) orange, slow blink +# component error purple, steady +# OTA blue, fast blink; green when done, 3 red blinks on error +# back to normal green for 1 s, then the light goes back to how it was +# packages: +# status_led: !include +# file: common/status_led.yaml +# vars: +# light_id: led_light +# Needs the ota id ota_esphome (common/ota_esp32.yaml). +defaults: + brightness: "0.3" + +globals: + - id: status_led_active + type: bool + initial_value: "false" + - id: status_led_saved + type: light::LightColorValues + - id: status_led_last + type: uint32_t + initial_value: "0xFFFFFFFF" + +script: + - id: status_led_show + parameters: + r: float + g: float + b: float + lit: bool + then: + - lambda: |- + auto *l = id(${light_id}); + uint32_t key = (lit << 24) | (uint32_t(r * 255) << 16) | (uint32_t(g * 255) << 8) | uint32_t(b * 255); + if (!id(status_led_active)) { + id(status_led_saved) = l->remote_values; + id(status_led_active) = true; + } else if (key == id(status_led_last)) { + return; // unchanged: don't flood Home Assistant with the same state + } + id(status_led_last) = key; + if (lit) { + l->turn_on().set_rgb(r, g, b).set_brightness(${brightness}).set_transition_length(0).set_effect("None").perform(); + } else { + l->turn_off().set_transition_length(0).perform(); + } + l->loop(); // write now: OTA blocks the main loop + - id: status_led_restore + parameters: + confirm: bool + then: + - lambda: |- + if (id(status_led_active) && confirm) id(status_led_show)->execute(0.0f, 1.0f, 0.0f, true); + - if: + condition: + lambda: 'return id(status_led_active) && confirm;' + then: + - delay: 1s # green: back to normal + - lambda: |- + if (!id(status_led_active)) return; + id(status_led_active) = false; + id(status_led_last) = 0xFFFFFFFF; + id(${light_id})->make_call().from_light_color_values(id(status_led_saved)).set_transition_length(0).perform(); + +interval: + - interval: 500ms + then: + - lambda: |- + static uint8_t tick = 0; + tick++; + bool slow = (tick / 2) % 2 == 0; // 1 s on, 1 s off + bool problem = !network::is_connected() || !api::global_api_server->is_connected() || + (App.get_app_state() & STATUS_LED_ERROR); + if (problem) id(status_led_restore)->stop(); + if (!network::is_connected()) { + id(status_led_show)->execute(1.0f, 0.0f, 0.0f, slow); + } else if (!api::global_api_server->is_connected()) { + id(status_led_show)->execute(1.0f, 0.4f, 0.0f, slow); + } else if (App.get_app_state() & STATUS_LED_ERROR) { + id(status_led_show)->execute(0.6f, 0.0f, 1.0f, true); + } else if (id(status_led_active) && !id(status_led_restore)->is_running()) { + id(status_led_restore)->execute(true); + } + +ota: + - id: !extend ota_esphome + on_begin: + - script.execute: + id: status_led_show + r: 0.0 + g: 0.0 + b: 1.0 + lit: true + on_progress: + - lambda: 'id(status_led_show)->execute(0.0f, 0.0f, 1.0f, (millis() / 250) % 2 == 0);' + on_end: + - script.execute: + id: status_led_show + r: 0.0 + g: 1.0 + b: 0.0 + lit: true + on_error: + - repeat: + count: 3 + then: + - script.execute: + id: status_led_show + r: 1.0 + g: 0.0 + b: 0.0 + lit: true + - delay: 250ms + - script.execute: + id: status_led_show + r: 1.0 + g: 0.0 + b: 0.0 + lit: false + - delay: 250ms + - script.execute: + id: status_led_restore + confirm: false diff --git a/mcu-echo.yaml b/mcu-echo.yaml index 4ed9e1a..844658d 100644 --- a/mcu-echo.yaml +++ b/mcu-echo.yaml @@ -5,7 +5,7 @@ # GPIO19 -> speaker_bus (i2s_audio i2s_bclk_pin) # GPIO21 Gateway LED ring red -> led_red (output ledc) # GPIO22 -> speaker_id (speaker i2s_audio i2s_dout_pin) -# GPIO23 Gateway LED ring blue -> led_status (light status_led); led_blue (output ledc) +# GPIO23 Gateway LED ring blue -> led_blue (output ledc) # GPIO25 Gateway LED ring green -> led_green (output ledc) # GPIO26 Grove SDA -> i2c (i2c sda) # GPIO27 ATOM onboard RGB LED (inside, not visible) @@ -33,6 +33,10 @@ esp32: packages: common: !include common/common.yaml ipv6_only: !include common/ipv6_only.yaml + status_led: !include + file: common/status_led.yaml + vars: + light_id: led_light external_components: # speaker media player: skip a failed playlist item (esphome#19862), drop once merged @@ -90,11 +94,6 @@ light: red: led_red green: led_green blue: led_blue - - platform: status_led - id: led_status - pin: - number: 23 - allow_other_uses: true output: - platform: ledc @@ -105,9 +104,7 @@ output: pin: 25 - platform: ledc id: led_blue - pin: - number: 23 - allow_other_uses: true + pin: 23 i2s_audio: - id: speaker_bus