diff --git a/esphome/common/api_common_noencryption.yaml b/esphome/common/api_common_noencryption.yaml new file mode 100644 index 0000000..83b85d0 --- /dev/null +++ b/esphome/common/api_common_noencryption.yaml @@ -0,0 +1,9 @@ +############################################# +# Enable the Home Assistant API +# https://esphome.io/components/api.html +############################################# +api: + #encryption: + #key: ${local_api_key} + reboot_timeout: 0s # disables watchdog reboot on API failure + diff --git a/esphome/common/mqtt_common.yaml b/esphome/common/mqtt_common.yaml index 0211701..82b5833 100644 --- a/esphome/common/mqtt_common.yaml +++ b/esphome/common/mqtt_common.yaml @@ -21,3 +21,4 @@ mqtt: discover_ip: False # enable device discovery (true is default) id: mqtt_client reboot_timeout: 0s # same for MQTT + log_topic: null # <— stops MQTT log streaming (big JSON payloads) diff --git a/esphome/common/sensors_common_lite.yaml b/esphome/common/sensors_common_lite.yaml new file mode 100644 index 0000000..7abd555 --- /dev/null +++ b/esphome/common/sensors_common_lite.yaml @@ -0,0 +1,41 @@ +########################################################################################## +# GENERAL COMMON SENSORS +# https://esphome.io/components/sensor/ +# LITE (1MB-friendly) +########################################################################################## +sensor: + - platform: uptime + name: "Uptime (s):" + update_interval: ${local_update_interval} + id: uptime_sensor + entity_category: diagnostic + + - platform: wifi_signal + name: "Wifi (dB):" + id: wifi_signal_db + update_interval: ${local_update_interval} + entity_category: diagnostic + +binary_sensor: + - platform: status + name: "Network Status" + icon: mdi:check-network-outline + entity_category: diagnostic + +########################################################################################## +# BUTTONS +# Diagnostic buttons, activated if needed in HA +########################################################################################## +button: + - platform: safe_mode + name: "Safe Mode Restart:" + entity_category: "diagnostic" + disabled_by_default: true + - platform: restart + name: "Restart:" + entity_category: "diagnostic" + disabled_by_default: true + - platform: factory_reset + name: "FACTORY RESET:" + entity_category: "diagnostic" + disabled_by_default: true diff --git a/esphome/esp-downstairskitchleds.yaml b/esphome/esp-downstairskitchleds.yaml index 8e6d8b6..d6dd744 100644 --- a/esphome/esp-downstairskitchleds.yaml +++ b/esphome/esp-downstairskitchleds.yaml @@ -83,12 +83,12 @@ substitutions: mqtt_local_device_command_OFF: "OFF" # Device Specific Settings - log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE - update_interval: "20s" # update time for for general sensors etc - led_gamma: "1.2" # Gamma from 1.2 - 3 is sensible to normalise the lED fading vs PWM - minimum_led_output: "1" # % If at this value or below, we'll switch it completely off - maximum_led_output: "90" # % Maximum output, it is sometimes nice to limit the output for longevity or aesthetic - + log_level: "NONE" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE + update_interval: "20s" # update time for for general sensors etc + led_gamma: "1.2" # Gamma from 1.2 - 3 is sensible to normalise the lED fading vs PWM + minimum_led_output: "1" # % If at this value or below, we'll switch it completely off + maximum_led_output: "90" # % Maximum output, it is sometimes nice to limit the output for longevity or aesthetic + max_on_default_hours: "6" # The max the LEDs will be on for, in case they get left on. 0 = no automatic turn-off ########################################################################################## # PACKAGES: Included Common Packages @@ -102,7 +102,8 @@ packages: local_static_ip_address: "${static_ip_address}" local_ota_pass: "${ota_pass}" common_api: !include - file: common/api_common.yaml + #file: common/api_common.yaml + file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" #common_webportal: !include @@ -114,7 +115,8 @@ packages: #common_sntp: !include # file: common/sntp_common.yaml common_general_sensors: !include - file: common/sensors_common.yaml + #file: common/sensors_common.yaml + file: common/sensors_common_lite.yaml vars: local_friendly_name: "${friendly_name}" local_update_interval: "${update_interval}" @@ -190,6 +192,16 @@ esphome: id: mosfet_leds transition_length: 0s - lambda: 'id(ramp_switch_target_on) = false;' + platformio_options: + build_unflags: + - -flto + build_flags: + - -fno-lto + - -Wl,--gc-sections + - -ffunction-sections + - -fdata-sections + - -DNDEBUG + ########################################################################################## # ESP PLATFORM AND FRAMEWORK @@ -221,6 +233,11 @@ globals: type: int restore_value: false initial_value: "${maximum_led_output}" # hard cap; never exceed this + # The maximum time the lights will stay on, in hours. Just in case they are left on. 0 = forever + - id: max_on_hours + type: int + restore_value: true + initial_value: '${max_on_default_hours}' # Default Fading Up Time (Selectable and will be retained) - id: ramp_up_ms # fade-in when turned ON type: int @@ -236,7 +253,7 @@ globals: type: int restore_value: true initial_value: '0' # default = Fade Up to Full (so can be deployed with no other setup) - + # Determine last fade direction. # true when you asked the light to end up ON (Ramp Up) # false when you asked the light to end up OFF (Ramp Down) @@ -448,7 +465,7 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 100ms # consider 200ms if you want fewer updates + update_interval: 250ms # consider 200ms if you want fewer updates lambda: |- const auto &cv = id(mosfet_leds).current_values; return cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f; @@ -480,18 +497,17 @@ sensor: unit_of_measurement: "%" icon: mdi:square-wave accuracy_decimals: 1 - update_interval: 100ms + update_interval: 250ms lambda: |- const auto &cv = id(mosfet_leds).current_values; if (!cv.is_on()) return 0.0f; - const float lin = cv.get_brightness(); // 0..1 (linear) - const float gamma = ${led_gamma}; // from substitutions - float pwm = powf(lin, gamma); // approximate PWM duty after gamma + const float lin = cv.get_brightness(); // 0..1 (linear brightness) + const float gamma = atof("${led_gamma}"); // parse substitution string → float + float pwm = powf(lin, gamma); // approx PWM duty after gamma if (pwm < 0.0f) pwm = 0.0f; if (pwm > 1.0f) pwm = 1.0f; return pwm * 100.0f; - ########################################################################################## # OUTPUT COMPONENT # https://esphome.io/components/light/index.html @@ -503,7 +519,7 @@ output: - platform: esp8266_pwm id: mosfet_pwm pin: GPIO4 - frequency: 2000 Hz # high frequency to avoid audible/visible artifacts + frequency: 500 Hz # high frequency to avoid audible/visible artifacts - platform: gpio id: green_led_out # Green LED pin: @@ -529,12 +545,19 @@ light: payload: "${mqtt_local_device_command_ON}" retain: true - lambda: 'id(ramp_switch_target_on) = true;' + - script.stop: max_on_watchdog + - if: + condition: + lambda: 'return id(max_on_hours) > 0;' + then: + - script.execute: max_on_watchdog on_turn_off: - mqtt.publish: topic: "${mqtt_local_status_topic}/light/state" payload: "${mqtt_local_device_command_OFF}" retain: true - lambda: 'id(ramp_switch_target_on) = false;' + - script.stop: max_on_watchdog on_state: - lambda: |- const float cap = id(max_brightness_pct) / 100.0f; @@ -630,7 +653,7 @@ number: - delay: 400ms - lambda: 'id(suppress_slider_sync) = false;' else: - # Map 1..100 → [min..max] and set ON + # Map 1..100 - [min..max] and set ON - lambda: |- id(suppress_slider_sync) = true; float pos = x; // 0..100 @@ -654,6 +677,36 @@ number: - delay: 400ms - lambda: 'id(suppress_slider_sync) = false;' + - platform: template + id: cfg_max_on_hours + name: "${friendly_name} Max On (h)" + entity_category: config + unit_of_measurement: h + icon: mdi:timer-cog + mode: slider + min_value: 0 + max_value: 48 + step: 1 + lambda: |- + return (float) id(max_on_hours); + set_action: + - lambda: |- + int hrs = (int) x; + if (hrs < 0) hrs = 0; + if (hrs > 48) hrs = 48; + id(max_on_hours) = hrs; + id(cfg_max_on_hours).publish_state((float) hrs); + - if: + condition: + lambda: 'return id(mosfet_leds).current_values.is_on();' + then: + - script.stop: max_on_watchdog + - if: + condition: + lambda: 'return id(max_on_hours) > 0;' + then: + - script.execute: max_on_watchdog + ########################################################################################## # SCRIPT COMPONENT # https://esphome.io/components/script.html @@ -757,4 +810,21 @@ script: auto call = id(mosfet_leds).make_call(); call.set_state(false); call.set_brightness(id(max_brightness_pct) / 100.0f); - call.perform(); \ No newline at end of file + call.perform(); + - id: max_on_watchdog + mode: restart + then: + - if: + condition: + lambda: 'return id(max_on_hours) > 0;' + then: + - delay: !lambda 'return (uint32_t) (id(max_on_hours) * 3600000UL);' + - if: + condition: + lambda: 'return id(mosfet_leds).current_values.is_on();' + then: + - lambda: |- + id(ramp_switch_target_on) = false; + id(mosfet_ramp_switch).publish_state(false); + - script.stop: ramp_on_script + - script.execute: ramp_off_script diff --git a/packages/health_checks.yaml b/packages/health_checks.yaml index 09e2ed3..d6b4ed4 100644 --- a/packages/health_checks.yaml +++ b/packages/health_checks.yaml @@ -1,24 +1,36 @@ +# ---- HTTP health probe ---- binary_sensor: - platform: rest - name: Node-RED Panda Up + name: Node-RED Panda Up (HTTP) resource: http://192.168.3.200:1880/healthz method: GET device_class: connectivity value_template: "{{ value_json.status == 'ok' }}" + timeout: 3 scan_interval: 30 +# ---- MQTT Will/Birth probe ---- mqtt: binary_sensor: - - name: "Node-RED Panda Up" - unique_id: "nodered_myhost_up" + - name: "Node-RED Panda Up (MQTT)" + unique_id: "nodered_panda_up_mqtt" state_topic: "nodered/panda/status" payload_on: "online" payload_off: "offline" device_class: connectivity qos: 1 + # Optional: avoid stale 'online' if no updates arrive + expire_after: 300 -healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:1880/healthz"] - interval: 30s - timeout: 3s - retries: 3 +# ---- Combine them (true if either is up) ---- +template: + - binary_sensor: + - name: "Node-RED Panda Up" + unique_id: "nodered_panda_up_combined" + device_class: connectivity + state: > + {{ is_state('binary_sensor.node_red_panda_up_http','on') + or is_state('binary_sensor.node_red_panda_up_mqtt','on') }} + availability: > + {{ states('binary_sensor.node_red_panda_up_http') not in ['unknown','unavailable'] + or states('binary_sensor.node_red_panda_up_mqtt') not in ['unknown','unavailable'] }}