From bcf514a04183fbb2b3c39f4a9ca0623558c485ed Mon Sep 17 00:00:00 2001 From: root Date: Sun, 12 Oct 2025 19:54:03 +1300 Subject: [PATCH] esphome pelmet LEDs, lounge scenes and other fixes --- .ha_run.lock | 2 +- .../common/include_debug_diag_sensors.yaml | 12 +- esphome/common/include_more_diag_sensors.yaml | 3 + esphome/esp-attobat.yaml.old | 217 ------ esphome/esp-attobattery.yaml | 100 ++- esphome/esp-breakfastbarleds.yaml | 59 +- esphome/esp-centralstairs-bottom.yaml | 8 +- esphome/esp-centralstairs-top.yaml | 8 +- esphome/esp-downstairskitchleds.yaml | 60 +- esphome/esp-leafbat.yaml | 248 ------- esphome/esp-leafbattery.yaml | 242 +++++++ esphome/esp-loungebookshelfleds.yaml | 60 +- esphome/esp-loungecabinetleds2.yaml | 55 +- ...etleds.yaml => esp-loungepelmetleds2.yaml} | 666 ++++++------------ esphome/esp-loungesouthleftswitch.yaml | 90 ++- esphome/esp-occupancystair.yaml | 2 +- packages/lounge_lighting_scenes.yaml | 160 +++-- packages/moonraker_3dprinter copy.yaml.bak | 370 ---------- packages/openhasp_lounge1_wallplate.yaml | 131 ++++ 19 files changed, 951 insertions(+), 1542 deletions(-) delete mode 100644 esphome/esp-attobat.yaml.old delete mode 100644 esphome/esp-leafbat.yaml create mode 100644 esphome/esp-leafbattery.yaml rename esphome/{esp-loungepelmetleds.yaml => esp-loungepelmetleds2.yaml} (63%) delete mode 100644 packages/moonraker_3dprinter copy.yaml.bak create mode 100644 packages/openhasp_lounge1_wallplate.yaml diff --git a/.ha_run.lock b/.ha_run.lock index 6a441fc..cf31149 100644 --- a/.ha_run.lock +++ b/.ha_run.lock @@ -1 +1 @@ -{"pid": 67, "version": 1, "ha_version": "2025.10.1", "start_ts": 1759647901.614362} \ No newline at end of file +{"pid": 68, "version": 1, "ha_version": "2025.10.2", "start_ts": 1760251204.265106} \ No newline at end of file diff --git a/esphome/common/include_debug_diag_sensors.yaml b/esphome/common/include_debug_diag_sensors.yaml index 1bfc7ee..dba197d 100644 --- a/esphome/common/include_debug_diag_sensors.yaml +++ b/esphome/common/include_debug_diag_sensors.yaml @@ -8,7 +8,7 @@ substitutions: # Debug helpers (loop time / heap / reset info) debug: - update_interval: ${local_update_interval} + update_interval: 5min sensor: # Free heap via debug (replaces template-based ESP.getFreeHeap if you prefer) @@ -16,12 +16,22 @@ sensor: free: name: "Free Heap" entity_category: "diagnostic" + disabled_by_default: true + filters: + - delta: 1024 # only if heap changes by ≥1 KB + - heartbeat: 5min # but still publish every 5 min + # Loop time via debug (replaces broken template loop time) - platform: debug loop_time: name: "Loop Time" + disabled_by_default: true entity_category: "diagnostic" + filters: + - delta: 2 # only if loop time changes by ≥2 ms + - heartbeat: 5min + ## If you’d rather keep the legacy heap sensor, uncomment below and ## delete the debug "free" block above to avoid duplicates: diff --git a/esphome/common/include_more_diag_sensors.yaml b/esphome/common/include_more_diag_sensors.yaml index f1cf0ab..19f6a3c 100644 --- a/esphome/common/include_more_diag_sensors.yaml +++ b/esphome/common/include_more_diag_sensors.yaml @@ -9,6 +9,7 @@ text_sensor: - platform: version name: "Version:" entity_category: "diagnostic" + disabled_by_default: true - platform: wifi_info ip_address: @@ -19,10 +20,12 @@ text_sensor: name: "Connected SSID" icon: mdi:wifi-strength-2 entity_category: diagnostic + disabled_by_default: true mac_address: name: "MAC Address:" icon: mdi:network-pos entity_category: diagnostic + disabled_by_default: true ##################################################################################################### # Creates a sensor of the uptime of the device, in formatted days, hours, minutes and seconds diff --git a/esphome/esp-attobat.yaml.old b/esphome/esp-attobat.yaml.old deleted file mode 100644 index 1587695..0000000 --- a/esphome/esp-attobat.yaml.old +++ /dev/null @@ -1,217 +0,0 @@ -############################################# -############################################# -# BYD ATTO3 12V Battery Monitor -# Monitoring the status of a vehicle 12V battery with -# an esp8266 (D1 Mini). It will obviously only -# transmit when the vehicle is within wifi range. -# Voltage is measured with a resistor voltage divider -# into the analogue GPIO on the esp8266. -# https://zorruno.com/2022/vehicle-12v-battery-monitoring/ -############################################## -############################################# - -############################################# -# Variable Substitutions -############################################# -substitutions: - devicename: "esp-attobat" - friendly_name: "Atto3 12V Battery Monitor" - description_comment: "Atto3 12V Battery Monitor (when home)" - api_key: !secret esp-attobat_api_key #unfortunately you can't use substitutions in secrets names - ota_pass: !secret esp-attobat_ota_pass #unfortunately you can't use substitutions in secrets names - wifi_ssid: !secret wifi_ssid - wifi_password: !secret wifi_password - fallback_ap_password: !secret fallback_ap_password - - #Add these if we are giving it a static ip, or remove them in the Wifi section - #A static IP will speed things up slightly in that it doesn't have to negotiate DHCP - static_ip_address: !secret esp-attobat_static_ip - static_ip_gateway: !secret esp-attobat_gateway - static_ip_subnet: !secret esp-attobat_subnet - - mqtt_server: !secret mqtt_server - mqtt_username: !secret mqtt_username - mqtt_password: !secret mqtt_password - mqtt_topic: "esphome" #main topic for the mqtt server, call it what you like - mqtt_commandstopic: "viewroad-commands" #main topic for commands (ie sleep), call it what you like - - #web_server_username: !secret web_server_username - #web_server_password: !secret web_server_password - - update_time: 10s #update time for for general temp sensors etc - -############################################# -# ESPHome -# https://esphome.io/components/esphome.html -############################################# -esphome: - name: ${devicename} - friendly_name: ${friendly_name} - comment: ${description_comment} #appears on the esphome page in HA - min_version: 2024.6.0 - -############################################# -# ESP Platform and Framework -# https://esphome.io/components/esp8266.html -# https://esphome.io/components/esp32.html -############################################# -esp8266: - board: d1_mini - framework: - version: latest #recommended, latest or dev - -############################################# -# ESPHome Logging Enable -# https://esphome.io/components/logger.html -############################################# -logger: - level: INFO #INFO Level suggested, or DEBUG for testing - #baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) - #esp8266_store_log_strings_in_flash: false - #tx_buffer_size: 64 - -############################################# -# Enable the Home Assistant API -# https://esphome.io/components/api.html -############################################# -api: - encryption: - key: ${api_key} - -############################################# -# Enable Over the Air Update Capability -# https://esphome.io/components/ota.html?highlight=ota -############################################# -ota: - - platform: esphome - password: ${ota_pass} - - platform: web_server # Uncomment if you want to be able to do OTA with the web interface - -############################################# -# Safe Mode -# Safe mode will detect boot loops -# https://esphome.io/components/safe_mode -############################################# -safe_mode: - -############################################# -# Wifi Settings -# https://esphome.io/components/wifi.html -# -# Power Save mode (can reduce wifi reliability) -# NONE (least power saving, Default for ESP8266) -# LIGHT (Default for ESP32) -# HIGH (most power saving) -############################################# -wifi: - ssid: ${wifi_ssid} - password: ${wifi_password} - #power_save_mode: LIGHT #https://esphome.io/components/wifi.html#wifi-power-save-mode - manual_ip: #optional static IP address - static_ip: ${static_ip_address} - gateway: ${static_ip_gateway} - subnet: ${static_ip_subnet} - ap: #Details for fallback hotspot (captive portal) in case wifi connection fails https://esphome.io/components/wifi.html#access-point-mode - ssid: ${devicename} fallback AP - password: ${fallback_ap_password} - ap_timeout: 30min #Time until it brings up fallback AP. default is 1min - -############################################# -# Web Portal for display and monitoring -############################################# -web_server: - port: 80 - #version: 2 - #include_internal: true - #ota: false - #auth: - #username: ${web_server_username} - #password: ${web_server_password} - -############################################# -# MQTT Monitoring -# https://esphome.io/components/mqtt.html?highlight=mqtt -# MUST also have api enabled if you enable MQTT -############################################# -mqtt: - broker: ${mqtt_server} - topic_prefix: ${mqtt_topic}/${devicename} - username: ${mqtt_username} - password: ${mqtt_password} - discovery: False # enable entity discovery (true is default, we don't want two HA Instances) - - # Availability Topic - birth_message: - topic: ${mqtt_topic}/${devicename}/availability - payload: online - will_message: - topic: ${mqtt_topic}/${devicename}/availability - payload: offline - - # A way to prevent deep sleep using MQTT command - on_message: - - topic: ${mqtt_commandstopic}/${devicename}/deepsleep - payload: "OFF" - then: - - deep_sleep.prevent: deep_sleep_1 - - topic: ${mqtt_commandstopic}/${devicename}/deepsleep - payload: "ON" - then: - - deep_sleep.enter: deep_sleep_1 - -# on_message: -# - topic: ${mqtt_commandstopic}/${devicename}/deepsleepon -# payload: 'ON' -# then: -# - deep_sleep.prevent: deep_sleep_1 -# - topic: ${mqtt_commandstopic}/${devicename}/deepsleepoff -# payload: 'OFF' -# then: -# - deep_sleep.enter: deep_sleep_1 - - - -######################################## -# Deep Sleep -# https://esphome.io/components/deep_sleep.html -######################################## -deep_sleep: - run_duration: 20s - sleep_duration: 5min - id: deep_sleep_1 - -############################################# -############################################# -# MAIN SENSORS -############################################# -############################################# -sensor: - #Quality of Wifi in dBm - - platform: wifi_signal - name: "WiFi Signal" - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - #Analog sensor for voltage reading (A0) - - platform: uptime - name: "Uptime" - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - - platform: adc - pin: A0 - name: "Battery Voltage" - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - filters: - - multiply: 3.3 #D1 mini V divider, 3.3V -> 1V on esp8266 - - calibrate_linear: #Read values with voltmeter and bench supply - - 3.11 -> 14.00 - - 3.00 -> 13.50 - - 2.89 -> 13.00 - - 2.77 -> 12.50 - - 2.67 -> 12.00 - - 2.55 -> 11.50 - - 2.45 -> 11.00 - - 2.34 -> 10.50 - - 2.22 -> 10.00 - - 2.11 -> 09.50 - - 2.00 -> 09.00 diff --git a/esphome/esp-attobattery.yaml b/esphome/esp-attobattery.yaml index dab905a..fb07b6b 100644 --- a/esphome/esp-attobattery.yaml +++ b/esphome/esp-attobattery.yaml @@ -34,20 +34,21 @@ substitutions: # MQTT LOCAL Controls mqtt_device_name: "atto-battery" - mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA - mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA + mqtt_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA + mqtt_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA # Device Settings - #relay_icon: "mdi:lightbulb-group" - log_level: "DEBUG" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE + log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for for general sensors etc + wake_interval: "20s" + sleep_interval: "300s" + mqtt_fail_sleep: "1h" # If MQTT is unavailable after boot wait, sleep this long + ########################################################################################## # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html ########################################################################################## -# Comment some of these out where not needed, eg MQTT, Web portal, SNTP, General Sensors -########################################################################################## packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### common_wifi: !include @@ -74,12 +75,12 @@ packages: common_webportal: !include common/webportal_common.yaml #### SNTP (Only use if you want/need accurate timeclocks) #### - common_sntp: !include common/sntp_common.yaml + #common_sntp: !include common/sntp_common.yaml #### DIAGNOSTICS Sensors #### diag_basic: !include common/include_basic_diag_sensors.yaml diag_more: !include common/include_more_diag_sensors.yaml - diag_debug: !include common/include_debug_diag_sensors.yaml + #diag_debug: !include common/include_debug_diag_sensors.yaml #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml ########################################################################################## @@ -91,6 +92,29 @@ esphome: friendly_name: ${friendly_name} comment: ${description_comment} #appears on the esphome page in HA min_version: 2024.6.0 + on_boot: + priority: 600 + then: + # Default: do NOT deep sleep until explicitly enabled via MQTT + - deep_sleep.prevent: deep_sleep_1 + - logger.log: "Deep sleep prevented by default on boot." + + # Fail-safe: wait up to 30s for MQTT; if not connected, sleep longer (backoff) + - wait_until: + condition: + mqtt.connected: + timeout: 30s + - if: + condition: + not: + mqtt.connected: + then: + - logger.log: "MQTT unreachable after 30s -> entering FALLBACK sleep for ${mqtt_fail_sleep}." + - deep_sleep.enter: + id: deep_sleep_1 + sleep_duration: ${mqtt_fail_sleep} # override only this cycle + else: + - logger.log: "MQTT connected; awaiting '${mqtt_command_topic}/deepsleep' command." ########################################################################################## # ESP Platform and Framework @@ -124,58 +148,51 @@ ota: # This adds device-specific MQTT command triggers to the common MQTT configuration. ########################################################################################## mqtt: - # Availability Topic birth_message: - topic: ${mqtt_local_status_topic}/availability + topic: ${mqtt_status_topic}/availability payload: online will_message: - topic: ${mqtt_local_status_topic}/availability + topic: ${mqtt_status_topic}/availability payload: offline - # A way to prevent deep sleep using MQTT command + # Control deep sleep over MQTT on_message: - - topic: ${mqtt_local_command_topic}/deepsleep + - topic: ${mqtt_command_topic}/deepsleep + payload: "ON" + then: + - deep_sleep.allow: deep_sleep_1 + - logger.log: "Deep sleep ALLOWED via MQTT. Device will sleep when run_duration elapses." + + - topic: ${mqtt_command_topic}/deepsleep payload: "OFF" then: - deep_sleep.prevent: deep_sleep_1 - - topic: ${mqtt_local_command_topic}/deepsleep - payload: "ON" - then: - - deep_sleep.enter: deep_sleep_1 + - logger.log: "Deep sleep PREVENTED via MQTT." -########################################################################################## +########################################################################################## # Deep Sleep # https://esphome.io/components/deep_sleep.html -########################################################################################## +########################################################################################## deep_sleep: - run_duration: 20s - sleep_duration: 5min id: deep_sleep_1 + run_duration: ${wake_interval} + sleep_duration: ${sleep_interval} ########################################################################################## # SENSOR COMPONENT # https://esphome.io/components/sensor/ ########################################################################################## sensor: - #Quality of Wifi in dBm - - platform: wifi_signal - name: "WiFi Signal" - update_interval: ${update_interval} - retain: true #Retain this as you'll have no value between sleeps otherwise - #Analog sensor for voltage reading (A0) - - platform: uptime - name: "Uptime" - update_interval: ${update_interval} - retain: true #Retain this as you'll have no value between sleeps otherwise - platform: adc pin: A0 name: "Battery Voltage" - update_interval: ${update_interval} - retain: true #Retain this as you'll have no value between sleeps otherwise + id: battery_voltage + update_interval: 4s + retain: true filters: - - multiply: 3.3 #D1 mini V divider, 3.3V -> 1V on esp8266 - - calibrate_linear: #Read values with voltmeter and bench supply + - multiply: 3.3 + - calibrate_linear: - 3.11 -> 14.00 - 3.00 -> 13.50 - 2.89 -> 13.00 @@ -187,3 +204,16 @@ sensor: - 2.22 -> 10.00 - 2.11 -> 09.50 - 2.00 -> 09.00 + - delta: 0.05 # publish immediately if change >= 0.05 V + - throttle: 2s # avoid bursts if noisy + on_value: + then: + - mqtt.publish: + topic: "${mqtt_status_topic}/batteryvoltage" + qos: 1 + retain: true + payload: !lambda |- + char buf[10]; + snprintf(buf, sizeof(buf), "%.2f", x); + return std::string(buf); + diff --git a/esphome/esp-breakfastbarleds.yaml b/esphome/esp-breakfastbarleds.yaml index 0a14847..a1078d3 100644 --- a/esphome/esp-breakfastbarleds.yaml +++ b/esphome/esp-breakfastbarleds.yaml @@ -83,13 +83,13 @@ substitutions: 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 aesthetics max_on_default_hours: "24" # The maximum time the LEDs will be on, in case they get left on. 0 = no automatic turn-off - pwm_frequency: "1000hz" # PWM output Frequency. High enough to avoid audible/visible artifacts + pwm_frequency: "1000" # PWM output Frequency in Hz. High enough to avoid audible/visible artifacts # Device Specific GPIO (so we can easily update for other devices) - device_status_led: GPIO02 # Can't really see if there is an LED + device_status_led: GPIO02 device_mosfet_out: GPIO12 - device_usr_button: GPIO04 # this is just a fake/safeish choice so it compiles with my generic yaml - device_fading_led: GPIO13 # this is just a fake/safeish choice so it compiles with my generic yaml + device_usr_button: GPIO04 # if no button, this is just a fake/safeish choice so it compiles with the generic yaml + device_fading_led: GPIO13 # if no LED, this is just a fake/safeish choice so it compiles with the generic yaml ########################################################################################## # PACKAGES: Included Common Packages @@ -124,8 +124,8 @@ packages: #### DIAGNOSTICS Sensors #### diag_basic: !include common/include_basic_diag_sensors.yaml - #diag_more: !include common/include_more_diag_sensors.yaml - #diag_debug: !include common/include_debug_diag_sensors.yaml + diag_more: !include common/include_more_diag_sensors.yaml + diag_debug: !include common/include_debug_diag_sensors.yaml #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml ########################################################################################## @@ -180,7 +180,7 @@ esphome: - lambda: 'id(booting) = false;' # NEW: force indicator OFF after boot logic - - output.turn_off: green_led_out + # (Removed: output.turn_off: green_led_out) # Only if you want to play with build flags... # platformio_options: @@ -228,6 +228,11 @@ globals: type: int restore_value: true initial_value: "${max_on_default_hours}" + # Compile time Gamma + - id: led_gamma_f + type: float + restore_value: false + initial_value: ${led_gamma} # <-- numeric at compile time # Default Fading Up Time (Selectable and will be retained) - id: ramp_up_ms # fade-in when turned ON @@ -389,7 +394,6 @@ button: # Stop any pending scripts (and their delayed actions) - script.stop: ramp_on_script - script.stop: ramp_off_script - - output.turn_off: green_led_out - lambda: |- // We are no longer ramping (up or down) id(ramping_for_off) = false; @@ -485,10 +489,11 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 1s # consider 200ms if you want fewer updates + update_interval: 2s # 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; + return cv.is_on() ? (float) (int)(cv.get_brightness() * 100.0f + 0.5f) : 0.0f; + on_value: then: - lambda: |- @@ -515,18 +520,16 @@ sensor: unit_of_measurement: "%" icon: mdi:square-wave accuracy_decimals: 1 - update_interval: 1s + update_interval: 2s 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 brightness - const float gamma = atof("${led_gamma}"); // parse substitution string -> float - float pwm = powf(lin, gamma); // approx PWM duty after gamma + const float lin = cv.get_brightness(); + float pwm = powf(lin, id(led_gamma_f)); 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 @@ -561,6 +564,7 @@ light: # ON: publish state, track intent, arm watchdog if configured on_turn_on: + - output.turn_on: green_led_out - mqtt.publish: topic: "${mqtt_local_status_topic}/light/state" payload: "${mqtt_local_device_command_ON}" @@ -580,6 +584,7 @@ light: # OFF: publish state, track intent, clear watchdog, and persist OFF so Mode 1 will not restore # Guarded by booting flag so the automatic OFF at boot from ALWAYS_OFF does not zero persistence. on_turn_off: + - output.turn_off: green_led_out - if: condition: lambda: 'return !id(booting);' @@ -600,13 +605,15 @@ light: - lambda: |- const float cap = id(max_brightness_pct) / 100.0f; const auto &cv = id(mosfet_leds).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.001f) return; // no call if already at/under cap + auto call = id(mosfet_leds).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); + ########################################################################################## # NUMBER COMPONENT @@ -768,7 +775,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at at least the floor without a visible "pop". - if: condition: @@ -805,7 +811,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -820,7 +825,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at the floor cleanly. - if: condition: @@ -863,7 +867,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -881,7 +884,6 @@ script: id(is_ramping) = true; id(ramping_for_off) = true; - script.stop: ramp_on_script - - output.turn_on: green_led_out - light.turn_on: id: mosfet_leds brightness: !lambda 'return id(min_brightness_pct) / 100.0f;' @@ -902,7 +904,6 @@ script: id: mosfet_leds transition_length: 150ms - delay: 150ms - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; id(ramping_for_off) = false; @@ -962,4 +963,4 @@ script: - light.turn_off: id: mosfet_leds transition_length: 0s - - output.turn_off: green_led_out # make sure LED is off if no ramp \ No newline at end of file + # (Removed: output.turn_off: green_led_out) # make sure LED is off if no ramp \ No newline at end of file diff --git a/esphome/esp-centralstairs-bottom.yaml b/esphome/esp-centralstairs-bottom.yaml index ee8d513..5de32e3 100644 --- a/esphome/esp-centralstairs-bottom.yaml +++ b/esphome/esp-centralstairs-bottom.yaml @@ -42,15 +42,15 @@ substitutions: # MQTT LOCAL Controls mqtt_local_device_name: "stair-mainlights-lower" - mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_local_device_name}/set" # Topic we will use to command this locally without HA - mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_local_device_name}/state" # Topic we will use to view status locally without HA + mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_local_device_name}" # Topic we will use to command this locally without HA + mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_local_device_name}" # Topic we will use to view status locally without HA mqtt_local_device_command_ON: "ON" mqtt_local_device_command_OFF: "OFF" # MQTT REMOTE Controls mqtt_remote_device1_name: "stair-footerlights" - mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}/set" - mqtt_remote_device1_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device1_name}/state" + mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}" + mqtt_remote_device1_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device1_name}" #mqtt_remote_device2_name: "stair-mainlights-upper" #mqtt_remote_device2_command_topic: "${mqtt_local_command_main_topic}/${mqtt_remote_device2_name}/relay3/set" #mqtt_remote_device2_status_topic: "${mqtt_local_status_main_topic}/${mqtt_remote_device2_name}/relay3/state" diff --git a/esphome/esp-centralstairs-top.yaml b/esphome/esp-centralstairs-top.yaml index 3ed486a..d10962d 100644 --- a/esphome/esp-centralstairs-top.yaml +++ b/esphome/esp-centralstairs-top.yaml @@ -42,15 +42,15 @@ substitutions: # MQTT LOCAL Controls mqtt_local_device_name: "stair-footerlights" - mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_local_device_name}/set" # Topic we will use to command this locally without HA - mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_local_device_name}/state" # Topic we will use to view status locally without HA + mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_local_device_name}" # Topic we will use to command this locally without HA + mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_local_device_name}" # Topic we will use to view status locally without HA mqtt_local_device_command_ON: "ON" mqtt_local_device_command_OFF: "OFF" # MQTT REMOTE Controls mqtt_remote_device1_name: "stair-mainlights-lower" - mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}/set" - mqtt_remote_device1_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device1_name}/state" + mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}" + mqtt_remote_device1_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device1_name}" #mqtt_remote_device2_name: "stair-mainlights-upper" #mqtt_remote_device2_command_topic: "${mqtt_local_command_main_topic}/${mqtt_remote_device2_name}/relay3/set" #mqtt_remote_device2_status_topic: "${mqtt_local_status_main_topic}/${mqtt_remote_device2_name}/relay3/state" diff --git a/esphome/esp-downstairskitchleds.yaml b/esphome/esp-downstairskitchleds.yaml index 00b8a54..00e12ad 100644 --- a/esphome/esp-downstairskitchleds.yaml +++ b/esphome/esp-downstairskitchleds.yaml @@ -94,13 +94,13 @@ substitutions: 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 aesthetics max_on_default_hours: "24" # The maximum time the LEDs will be on, in case they get left on. 0 = no automatic turn-off - pwm_frequency: "1000 hz" # PWM output Frequency. High enough to avoid audible/visible artifacts + pwm_frequency: "1000" # PWM output Frequency in Hz. High enough to avoid audible/visible artifacts # Device Specific GPIO (so we can easily update for other devices) device_status_led: GPIO02 device_mosfet_out: GPIO04 - device_usr_button: GPIO12 - device_fading_led: GPIO13 + device_usr_button: GPIO12 # if no button, this is just a fake/safeish choice so it compiles with the generic yaml + device_fading_led: GPIO13 # if no LED, this is just a fake/safeish choice so it compiles with the generic yaml ########################################################################################## # PACKAGES: Included Common Packages @@ -117,8 +117,8 @@ packages: #### HOME ASSISTANT API (choose encryption or no encryption options) #### common_api: !include - file: common/api_common.yaml - #file: common/api_common_noencryption.yaml + #file: common/api_common.yaml + file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" @@ -137,7 +137,7 @@ packages: diag_basic: !include common/include_basic_diag_sensors.yaml diag_more: !include common/include_more_diag_sensors.yaml diag_debug: !include common/include_debug_diag_sensors.yaml - diag_resetcount: !include common/include_resetcount_diag_sensors.yaml + #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml ########################################################################################## # ESPHome CORE CONFIGURATION @@ -191,7 +191,7 @@ esphome: - lambda: 'id(booting) = false;' # NEW: force indicator OFF after boot logic - - output.turn_off: green_led_out + # (Removed: output.turn_off: green_led_out) # Only if you want to play with build flags... # platformio_options: @@ -239,6 +239,11 @@ globals: type: int restore_value: true initial_value: "${max_on_default_hours}" + # Compile time Gamma + - id: led_gamma_f + type: float + restore_value: false + initial_value: ${led_gamma} # <-- numeric at compile time # Default Fading Up Time (Selectable and will be retained) - id: ramp_up_ms # fade-in when turned ON @@ -400,7 +405,6 @@ button: # Stop any pending scripts (and their delayed actions) - script.stop: ramp_on_script - script.stop: ramp_off_script - - output.turn_off: green_led_out - lambda: |- // We are no longer ramping (up or down) id(ramping_for_off) = false; @@ -496,10 +500,11 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 1s # consider 200ms if you want fewer updates + update_interval: 2s # 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; + return cv.is_on() ? (float) (int)(cv.get_brightness() * 100.0f + 0.5f) : 0.0f; + on_value: then: - lambda: |- @@ -526,18 +531,16 @@ sensor: unit_of_measurement: "%" icon: mdi:square-wave accuracy_decimals: 1 - update_interval: 1s + update_interval: 2s 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 brightness - const float gamma = atof("${led_gamma}"); // parse substitution string -> float - float pwm = powf(lin, gamma); // approx PWM duty after gamma + const float lin = cv.get_brightness(); + float pwm = powf(lin, id(led_gamma_f)); 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 @@ -572,6 +575,7 @@ light: # ON: publish state, track intent, arm watchdog if configured on_turn_on: + - output.turn_on: green_led_out - mqtt.publish: topic: "${mqtt_local_status_topic}/light/state" payload: "${mqtt_local_device_command_ON}" @@ -591,6 +595,7 @@ light: # OFF: publish state, track intent, clear watchdog, and persist OFF so Mode 1 will not restore # Guarded by booting flag so the automatic OFF at boot from ALWAYS_OFF does not zero persistence. on_turn_off: + - output.turn_off: green_led_out - if: condition: lambda: 'return !id(booting);' @@ -611,13 +616,15 @@ light: - lambda: |- const float cap = id(max_brightness_pct) / 100.0f; const auto &cv = id(mosfet_leds).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.001f) return; // no call if already at/under cap + auto call = id(mosfet_leds).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); + ########################################################################################## # NUMBER COMPONENT @@ -779,7 +786,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at at least the floor without a visible "pop". - if: condition: @@ -816,7 +822,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -831,7 +836,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at the floor cleanly. - if: condition: @@ -874,7 +878,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -892,7 +895,6 @@ script: id(is_ramping) = true; id(ramping_for_off) = true; - script.stop: ramp_on_script - - output.turn_on: green_led_out - light.turn_on: id: mosfet_leds brightness: !lambda 'return id(min_brightness_pct) / 100.0f;' @@ -913,7 +915,6 @@ script: id: mosfet_leds transition_length: 150ms - delay: 150ms - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; id(ramping_for_off) = false; @@ -973,5 +974,4 @@ script: - light.turn_off: id: mosfet_leds transition_length: 0s - - output.turn_off: green_led_out # make sure LED is off if no ramp - + # (Removed: output.turn_off: green_led_out) # make sure LED is off if no ramp \ No newline at end of file diff --git a/esphome/esp-leafbat.yaml b/esphome/esp-leafbat.yaml deleted file mode 100644 index e15e1d3..0000000 --- a/esphome/esp-leafbat.yaml +++ /dev/null @@ -1,248 +0,0 @@ -############################################# -############################################# -# Nissan Leaf 12V Battery Monitor -# Monitoring the status of a vehicle 12V battery with -# an esp8266 (D1 Mini). It will obviously only -# transmit when the vehicle is within wifi range. -# Voltage is measured with a resistor voltage divider -# into the analogue GPIO on the esp8266. -# https://zorruno.com/2022/vehicle-12v-battery-monitoring/ -############################################## -############################################# - -############################################# -# Variable Substitutions -############################################# -substitutions: - devicename: "esp-leafbat" - friendly_name: "Nissan Leaf 12V Battery Monitor" - description_comment: "Nissan Leaf 12V Battery Monitor (when home)" - api_key: !secret esp-leafbat_api_key #unfortunately you can't use substitutions in secrets names - ota_pass: !secret esp-leafbat_ota_pass #unfortunately you can't use substitutions in secrets names - wifi_ssid: !secret wifi_ssid - wifi_password: !secret wifi_password - fallback_ap_password: !secret fallback_ap_password - - #Add these if we are giving it a static ip, or remove them in the Wifi section - #A static IP will speed things up slightly in that it doesn't have to negotiate DHCP - static_ip_address: !secret esp-leafbat_static_ip - static_ip_gateway: !secret esp-leafbat_gateway - static_ip_subnet: !secret esp-leafbat_subnet - - mqtt_server: !secret mqtt_server - mqtt_username: !secret mqtt_username - mqtt_password: !secret mqtt_password - mqtt_topic: "esphome" #main topic for the mqtt server, call it what you like - mqtt_commandstopic: "viewroad-commands" #main topic for commands (ie sleep), call it what you like - - #web_server_username: !secret web_server_username - #web_server_password: !secret web_server_password - - update_time: 10s #update time for for general temp sensors etc - -############################################# -# ESPHome -# https://esphome.io/components/esphome.html -############################################# -esphome: - name: ${devicename} - friendly_name: ${friendly_name} - comment: ${description_comment} #appears on the esphome page in HA - min_version: 2024.6.0 - -############################################# -# ESP Platform and Framework -# https://esphome.io/components/esp8266.html -# https://esphome.io/components/esp32.html -############################################# -esp8266: - board: d1_mini - framework: - version: latest #recommended, latest or dev - -############################################# -# ESPHome Logging Enable -# https://esphome.io/components/logger.html -############################################# -logger: - level: INFO #INFO Level suggested, or DEBUG for testing - #baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) - #esp8266_store_log_strings_in_flash: false - #tx_buffer_size: 64 - -#uart: -# id: uart_output -# tx_pin: GPIO12 -# rx_pin: GPIO13 -# baud_rate: 9600 - -############################################# -# Enable the Home Assistant API -# https://esphome.io/components/api.html -############################################# -api: - encryption: - key: ${api_key} - -############################################# -# Enable Over the Air Update Capability -# https://esphome.io/components/ota.html?highlight=ota -############################################# -ota: - - platform: esphome - password: ${ota_pass} - -############################################# -# Safe Mode -# Safe mode will detect boot loops -# https://esphome.io/components/safe_mode -############################################# -safe_mode: - -############################################# -# Wifi Settings -# https://esphome.io/components/wifi.html -# -# Power Save mode (can reduce wifi reliability) -# NONE (least power saving, Default for ESP8266) -# LIGHT (Default for ESP32) -# HIGH (most power saving) -############################################# -wifi: - ssid: ${wifi_ssid} - password: ${wifi_password} - #power_save_mode: LIGHT #https://esphome.io/components/wifi.html#wifi-power-save-mode - manual_ip: #optional static IP address - static_ip: ${static_ip_address} - gateway: ${static_ip_gateway} - subnet: ${static_ip_subnet} - ap: #Details for fallback hotspot (captive portal) in case wifi connection fails https://esphome.io/components/wifi.html#access-point-mode - ssid: ${devicename} fallback AP - password: ${fallback_ap_password} - ap_timeout: 30min #Time until it brings up fallback AP. default is 1min - -############################################# -# Web Portal for display and monitoring -############################################# -web_server: - port: 80 - #version: 2 - #include_internal: true - #ota: false - #auth: - #username: ${web_server_username} - #password: ${web_server_password} - -############################################# -# MQTT Monitoring -# https://esphome.io/components/mqtt.html?highlight=mqtt -# MUST also have api enabled if you enable MQTT -############################################# -mqtt: - broker: ${mqtt_server} - topic_prefix: ${mqtt_topic}/${devicename} - username: ${mqtt_username} - password: ${mqtt_password} - discovery: False # enable entity discovery (true is default, we don't want two HA Instances) - - # Availability Topic - birth_message: - topic: ${mqtt_topic}/${devicename}/availability - payload: online - will_message: - topic: ${mqtt_topic}/${devicename}/availability - payload: offline - - # A way to prevent deep sleep using MQTT command - on_message: - - topic: ${mqtt_commandstopic}/${devicename}/deepsleep - payload: 'OFF' - then: - - deep_sleep.prevent: deep_sleep_1 - - topic: ${mqtt_commandstopic}/${devicename}/deepsleep - payload: 'ON' - then: - - deep_sleep.enter: deep_sleep_1 - -######################################## -# Deep Sleep -# https://esphome.io/components/deep_sleep.html -######################################## -deep_sleep: - run_duration: 20s - sleep_duration: 5min - id: deep_sleep_1 - -#time: -# - platform: homeassistant -# id: ha_time -# timezone: Pacific/Auckland - -#text_sensor: -# - platform: template -# name: "Current time" -# id: current_time -# lambda: return id(ha_time).now().strftime("%H%M"); -# lambda: |- -# char str[17]; - # time_t currTime = id(ha_time).now().timestamp; - # strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&currTime)); - # return { str }; -# update_interval: ${update_time} - -############################################# -############################################# -# MAIN SENSORS -############################################# -############################################# -sensor: - #Quality of Wifi in dBm - - platform: wifi_signal - name: "WiFi Signal" - id: wifi_strength - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - #Analog sensor for voltage reading (A0) - - platform: uptime - name: "Uptime" - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - - platform: adc - pin: A0 - name: "Battery Voltage" - id: battery_voltage - #mqtt_id: battery_voltage - update_interval: ${update_time} - retain: true #Retain this as you'll have no value between sleeps otherwise - # This set used a voltage divider with a 100k and a 4.7k R value - filters: - - multiply: 3.3 #D1 mini V divider, 3.3V -> 1V on esp8266 - - calibrate_linear: #Read values with voltmeter and bench supply - - 0.72 -> 16.00 - - 0.68 -> 15.00 - - 0.63 -> 14.00 - - 0.59 -> 13.00 - - 0.55 -> 12.00 - - 0.51 -> 11.00 - - 0.45 -> 10.00 - - 0.40 -> 09.00 - - 0.36 -> 08.00 - - 0.32 -> 07.00 - - 0.28 -> 06.00 - - 0.23 -> 05.00 -# on_value: -# - logger.log: -# level: INFO -# format: "time,%s,battery,%.1f,wifi,%.1f" -# args: [ 'id(ha_time).now()', 'id(battery_voltage).state', 'id(wifi_strength).state' ] - -# - uart.write: -# id: uart_output -# data: !lambda char buf[128]; -# sprintf(buf, "%s", id(battery_voltage).state); -# std::string s = buf; -# return std::vector( s.begin(), s.end() ); -# - logger.log: -# level: INFO -# format: "time,%.1f,battery,%.1f,wifi,%.1f" -# args: [ 'id(sntp_time).now()', 'id(battery_voltage).state', 'id(wifi_strength).state' ] diff --git a/esphome/esp-leafbattery.yaml b/esphome/esp-leafbattery.yaml new file mode 100644 index 0000000..849bf14 --- /dev/null +++ b/esphome/esp-leafbattery.yaml @@ -0,0 +1,242 @@ +########################################################################################## +########################################################################################## +# Nissan Leaf 12V Battery Monitor +# Monitoring the status of a vehicle 12V battery with +# an esp8266 (D1 Mini). It will obviously only +# transmit when the vehicle is within wifi range. +# Voltage is measured with a resistor voltage divider +# into the analogue GPIO on the esp8266. +# https://zorruno.com/2022/vehicle-12v-battery-monitoring/ +########################################################################################## +########################################################################################## + +########################################################################################## +# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS +# If NOT using a secrets file, just replace these with the passwords etc (in quotes) +########################################################################################## +substitutions: + # Device Naming + device_name: "esp-leafbattery" + friendly_name: "Nissan Leaf 12V Battery Monitor" + description_comment: "Nissan Leaf 12V Battery Monitor (when home)" + device_area: "Garage" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + + # Project Naming + project_name: "Generic ESP8266.D1 Mini" # Project Details + project_version: "v1.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + + # Passwords & Secrets + api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names + ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names + static_ip_address: !secret esp-leafbattery_ip + mqtt_command_main_topic: !secret mqtt_command_main_topic + mqtt_status_main_topic: !secret mqtt_status_main_topic + + # MQTT LOCAL Controls + mqtt_device_name: "leaf-battery" + mqtt_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA + mqtt_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA + + # Device Settings + #relay_icon: "mdi:lightbulb-group" + log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE + update_interval: "60s" # update time for for general sensors etc + + wake_interval: "20s" + sleep_interval: "300s" + mqtt_fail_sleep: "1h" # If MQTT is unavailable after boot wait, sleep this long + +########################################################################################## +# PACKAGES: Included Common Packages +# https://esphome.io/components/packages.html +########################################################################################## +# Comment some of these out where not needed, eg MQTT, Web portal, SNTP, General Sensors +########################################################################################## +packages: + #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### + common_wifi: !include + file: common/network_common.yaml + vars: + local_device_name: "${device_name}" + local_static_ip_address: "${static_ip_address}" + local_ota_pass: "${ota_pass}" + + #### HOME ASSISTANT API (choose encryption or no encryption options) #### + common_api: !include + file: common/api_common.yaml + #file: common/api_common_noencryption.yaml + vars: + local_api_key: "${api_key}" + + #### MQTT #### + common_mqtt: !include + file: common/mqtt_common.yaml + vars: + local_device_name: "${device_name}" + + #### WEB PORTAL #### + common_webportal: !include common/webportal_common.yaml + + #### SNTP (Only use if you want/need accurate timeclocks) #### + #common_sntp: !include common/sntp_common.yaml + + #### DIAGNOSTICS Sensors #### + diag_basic: !include common/include_basic_diag_sensors.yaml + diag_more: !include common/include_more_diag_sensors.yaml + #diag_debug: !include common/include_debug_diag_sensors.yaml + #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml + +########################################################################################## +# ESPHome +# https://esphome.io/components/esphome.html +########################################################################################## +esphome: + name: ${device_name} + friendly_name: ${friendly_name} + comment: ${description_comment} #appears on the esphome page in HA + min_version: 2024.6.0 + on_boot: + priority: 600 + then: + # Default: do NOT deep sleep until explicitly enabled via MQTT + - deep_sleep.prevent: deep_sleep_1 + - logger.log: "Deep sleep prevented by default on boot." + + # Fail-safe: wait up to 30s for MQTT; if not connected, sleep longer (backoff) + - wait_until: + condition: + mqtt.connected: + timeout: 30s + - if: + condition: + not: + mqtt.connected: + then: + - logger.log: "MQTT unreachable after 30s -> entering FALLBACK sleep for ${mqtt_fail_sleep}." + - deep_sleep.enter: + id: deep_sleep_1 + sleep_duration: ${mqtt_fail_sleep} # override only this cycle + else: + - logger.log: "MQTT connected; awaiting '${mqtt_command_topic}/deepsleep' command." + +########################################################################################## +# ESP Platform and Framework +# https://esphome.io/components/esp8266.html +# https://esphome.io/components/esp32.html +########################################################################################## +esp8266: + board: d1_mini + framework: + version: latest #recommended, latest or dev + +########################################################################################## +# ESPHome Logging Enable +# https://esphome.io/components/logger.html +########################################################################################## +logger: + level: "${log_level}" #INFO Level suggested, or DEBUG for testing + #baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) + #esp8266_store_log_strings_in_flash: false + #tx_buffer_size: 64 + +############################################# +# Enable Over the Air Update Capability +# https://esphome.io/components/ota.html?highlight=ota +############################################# +ota: + - platform: web_server # Uncomment if you want to be able to do OTA with the web interface + +########################################################################################## +# MQTT COMMANDS +# This adds device-specific MQTT command triggers to the common MQTT configuration. +########################################################################################## +mqtt: + # Availability Topic + birth_message: + topic: ${mqtt_status_topic}/availability + payload: online + will_message: + topic: ${mqtt_status_topic}/availability + payload: offline + + # Control deep sleep over MQTT + on_message: + - topic: ${mqtt_command_topic}/deepsleep + payload: "ON" + then: + - deep_sleep.allow: deep_sleep_1 + - logger.log: "Deep sleep ALLOWED via MQTT. Device will sleep when run_duration elapses." + + - topic: ${mqtt_command_topic}/deepsleep + payload: "OFF" + then: + - deep_sleep.prevent: deep_sleep_1 + - logger.log: "Deep sleep PREVENTED via MQTT." + +########################################################################################## +# Deep Sleep +# https://esphome.io/components/deep_sleep.html +########################################################################################## +deep_sleep: + id: deep_sleep_1 + run_duration: ${wake_interval} + sleep_duration: ${sleep_interval} + +########################################################################################## +# SENSOR COMPONENT +# https://esphome.io/components/sensor/ +########################################################################################## +sensor: + - platform: adc + pin: A0 + name: "Battery Voltage" + id: battery_voltage + update_interval: 4s + retain: true + filters: + - multiply: 3.3 + - calibrate_linear: + - 0.72 -> 16.00 + - 0.68 -> 15.00 + - 0.63 -> 14.00 + - 0.59 -> 13.00 + - 0.55 -> 12.00 + - 0.51 -> 11.00 + - 0.45 -> 10.00 + - 0.40 -> 09.00 + - 0.36 -> 08.00 + - 0.32 -> 07.00 + - 0.28 -> 06.00 + - 0.23 -> 05.00 + - delta: 0.05 # publish immediately if change >= 0.05 V + - throttle: 2s # avoid bursts if noisy + on_value: + then: + - mqtt.publish: + topic: "${mqtt_status_topic}/batteryvoltage" + qos: 1 + retain: true + payload: !lambda |- + char buf[10]; + snprintf(buf, sizeof(buf), "%.2f", x); + return std::string(buf); + +########################################################################################## +# INTERVAL COMPONENT +########################################################################################## +interval: + - interval: 60s + then: + - mqtt.publish: + topic: "${mqtt_status_topic}/batteryvoltage" + qos: 1 + retain: true + payload: !lambda |- + const float v = id(battery_voltage).state; // current filtered state + char buf[10]; + snprintf(buf, sizeof(buf), "%.2f", v); + return std::string(buf); + + + + diff --git a/esphome/esp-loungebookshelfleds.yaml b/esphome/esp-loungebookshelfleds.yaml index ce83182..339bce7 100644 --- a/esphome/esp-loungebookshelfleds.yaml +++ b/esphome/esp-loungebookshelfleds.yaml @@ -83,13 +83,13 @@ substitutions: 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 aesthetics max_on_default_hours: "24" # The maximum time the LEDs will be on, in case they get left on. 0 = no automatic turn-off - pwm_frequency: "1000hz" # PWM output Frequency. High enough to avoid audible/visible artifacts + pwm_frequency: "1000" # PWM output Frequency in Hz. High enough to avoid audible/visible artifacts # Device Specific GPIO (so we can easily update for other devices) - device_status_led: GPIO02 # Can't really see if there is an LED + device_status_led: GPIO02 device_mosfet_out: GPIO12 - device_usr_button: GPIO04 # this is just a fake/safeish choice so it compiles with my generic yaml - device_fading_led: GPIO13 # this is just a fake/safeish choice so it compiles with my generic yaml + device_usr_button: GPIO04 # if no button, this is just a fake/safeish choice so it compiles with the generic yaml + device_fading_led: GPIO13 # if no LED, this is just a fake/safeish choice so it compiles with the generic yaml ########################################################################################## # PACKAGES: Included Common Packages @@ -124,8 +124,8 @@ packages: #### DIAGNOSTICS Sensors #### diag_basic: !include common/include_basic_diag_sensors.yaml - #diag_more: !include common/include_more_diag_sensors.yaml - #diag_debug: !include common/include_debug_diag_sensors.yaml + diag_more: !include common/include_more_diag_sensors.yaml + diag_debug: !include common/include_debug_diag_sensors.yaml #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml ########################################################################################## @@ -180,7 +180,7 @@ esphome: - lambda: 'id(booting) = false;' # NEW: force indicator OFF after boot logic - - output.turn_off: green_led_out + # (Removed: output.turn_off: green_led_out) # Only if you want to play with build flags... # platformio_options: @@ -228,6 +228,11 @@ globals: type: int restore_value: true initial_value: "${max_on_default_hours}" + # Compile time Gamma + - id: led_gamma_f + type: float + restore_value: false + initial_value: ${led_gamma} # <-- numeric at compile time # Default Fading Up Time (Selectable and will be retained) - id: ramp_up_ms # fade-in when turned ON @@ -389,7 +394,6 @@ button: # Stop any pending scripts (and their delayed actions) - script.stop: ramp_on_script - script.stop: ramp_off_script - - output.turn_off: green_led_out - lambda: |- // We are no longer ramping (up or down) id(ramping_for_off) = false; @@ -485,10 +489,11 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 1s # consider 200ms if you want fewer updates + update_interval: 2s # 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; + return cv.is_on() ? (float) (int)(cv.get_brightness() * 100.0f + 0.5f) : 0.0f; + on_value: then: - lambda: |- @@ -515,18 +520,16 @@ sensor: unit_of_measurement: "%" icon: mdi:square-wave accuracy_decimals: 1 - update_interval: 1s + update_interval: 2s 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 brightness - const float gamma = atof("${led_gamma}"); // parse substitution string -> float - float pwm = powf(lin, gamma); // approx PWM duty after gamma + const float lin = cv.get_brightness(); + float pwm = powf(lin, id(led_gamma_f)); 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 @@ -561,6 +564,7 @@ light: # ON: publish state, track intent, arm watchdog if configured on_turn_on: + - output.turn_on: green_led_out - mqtt.publish: topic: "${mqtt_local_status_topic}/light/state" payload: "${mqtt_local_device_command_ON}" @@ -580,6 +584,7 @@ light: # OFF: publish state, track intent, clear watchdog, and persist OFF so Mode 1 will not restore # Guarded by booting flag so the automatic OFF at boot from ALWAYS_OFF does not zero persistence. on_turn_off: + - output.turn_off: green_led_out - if: condition: lambda: 'return !id(booting);' @@ -600,13 +605,15 @@ light: - lambda: |- const float cap = id(max_brightness_pct) / 100.0f; const auto &cv = id(mosfet_leds).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.001f) return; // no call if already at/under cap + auto call = id(mosfet_leds).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); + ########################################################################################## # NUMBER COMPONENT @@ -768,7 +775,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at at least the floor without a visible "pop". - if: condition: @@ -805,7 +811,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -820,7 +825,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at the floor cleanly. - if: condition: @@ -863,7 +867,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -881,7 +884,6 @@ script: id(is_ramping) = true; id(ramping_for_off) = true; - script.stop: ramp_on_script - - output.turn_on: green_led_out - light.turn_on: id: mosfet_leds brightness: !lambda 'return id(min_brightness_pct) / 100.0f;' @@ -902,7 +904,6 @@ script: id: mosfet_leds transition_length: 150ms - delay: 150ms - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; id(ramping_for_off) = false; @@ -962,5 +963,4 @@ script: - light.turn_off: id: mosfet_leds transition_length: 0s - - output.turn_off: green_led_out # make sure LED is off if no ramp - + # (Removed: output.turn_off: green_led_out) # make sure LED is off if no ramp \ No newline at end of file diff --git a/esphome/esp-loungecabinetleds2.yaml b/esphome/esp-loungecabinetleds2.yaml index d3de992..ffb8e5a 100644 --- a/esphome/esp-loungecabinetleds2.yaml +++ b/esphome/esp-loungecabinetleds2.yaml @@ -83,18 +83,18 @@ substitutions: # Device Specific Settings log_level: "NONE" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (default), VERBOSE, VERY_VERBOSE - update_interval: "20s" # Update time for general sensors, etc. + update_interval: "30s" # Update time 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 aesthetics max_on_default_hours: "24" # The maximum time the LEDs will be on, in case they get left on. 0 = no automatic turn-off - pwm_frequency: "1000hz" # PWM output Frequency. High enough to avoid audible/visible artifacts + pwm_frequency: "1000" # PWM output Frequency in Hz. High enough to avoid audible/visible artifacts # Device Specific GPIO (so we can easily update for other devices) device_status_led: GPIO02 device_mosfet_out: GPIO04 - device_usr_button: GPIO12 - device_fading_led: GPIO13 + device_usr_button: GPIO12 # if no button, this is just a fake/safeish choice so it compiles with the generic yaml + device_fading_led: GPIO13 # if no LED, this is just a fake/safeish choice so it compiles with the generic yaml ########################################################################################## # PACKAGES: Included Common Packages @@ -185,7 +185,7 @@ esphome: - lambda: 'id(booting) = false;' # NEW: force indicator OFF after boot logic - - output.turn_off: green_led_out + # (Removed: output.turn_off: green_led_out) # Only if you want to play with build flags... # platformio_options: @@ -233,6 +233,11 @@ globals: type: int restore_value: true initial_value: "${max_on_default_hours}" + # Compile time Gamma + - id: led_gamma_f + type: float + restore_value: false + initial_value: ${led_gamma} # <-- numeric at compile time # Default Fading Up Time (Selectable and will be retained) - id: ramp_up_ms # fade-in when turned ON @@ -394,7 +399,6 @@ button: # Stop any pending scripts (and their delayed actions) - script.stop: ramp_on_script - script.stop: ramp_off_script - - output.turn_off: green_led_out - lambda: |- // We are no longer ramping (up or down) id(ramping_for_off) = false; @@ -490,10 +494,11 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 1s # consider 200ms if you want fewer updates + update_interval: 2s # 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; + return cv.is_on() ? (float) (int)(cv.get_brightness() * 100.0f + 0.5f) : 0.0f; + on_value: then: - lambda: |- @@ -520,18 +525,16 @@ sensor: unit_of_measurement: "%" icon: mdi:square-wave accuracy_decimals: 1 - update_interval: 1s + update_interval: 2s 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 brightness - const float gamma = atof("${led_gamma}"); // parse substitution string -> float - float pwm = powf(lin, gamma); // approx PWM duty after gamma + const float lin = cv.get_brightness(); + float pwm = powf(lin, id(led_gamma_f)); 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 @@ -566,6 +569,7 @@ light: # ON: publish state, track intent, arm watchdog if configured on_turn_on: + - output.turn_on: green_led_out - mqtt.publish: topic: "${mqtt_local_status_topic}/light/state" payload: "${mqtt_local_device_command_ON}" @@ -585,6 +589,7 @@ light: # OFF: publish state, track intent, clear watchdog, and persist OFF so Mode 1 will not restore # Guarded by booting flag so the automatic OFF at boot from ALWAYS_OFF does not zero persistence. on_turn_off: + - output.turn_off: green_led_out - if: condition: lambda: 'return !id(booting);' @@ -605,13 +610,15 @@ light: - lambda: |- const float cap = id(max_brightness_pct) / 100.0f; const auto &cv = id(mosfet_leds).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.001f) return; // no call if already at/under cap + auto call = id(mosfet_leds).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); + ########################################################################################## # NUMBER COMPONENT @@ -773,7 +780,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at at least the floor without a visible "pop". - if: condition: @@ -810,7 +816,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -825,7 +830,6 @@ script: then: - lambda: 'id(is_ramping) = true;' - script.stop: ramp_off_script - - output.turn_on: green_led_out # Ensure we start at the floor cleanly. - if: condition: @@ -868,7 +872,6 @@ script: id(last_ramp_ms) = (int) (id(ramp_up_ms) * frac); return (uint32_t) id(last_ramp_ms); - delay: !lambda 'return (uint32_t) id(last_ramp_ms);' - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; const auto &cv = id(mosfet_leds).current_values; @@ -886,7 +889,6 @@ script: id(is_ramping) = true; id(ramping_for_off) = true; - script.stop: ramp_on_script - - output.turn_on: green_led_out - light.turn_on: id: mosfet_leds brightness: !lambda 'return id(min_brightness_pct) / 100.0f;' @@ -907,7 +909,6 @@ script: id: mosfet_leds transition_length: 150ms - delay: 150ms - - output.turn_off: green_led_out - lambda: |- id(is_ramping) = false; id(ramping_for_off) = false; @@ -967,4 +968,4 @@ script: - light.turn_off: id: mosfet_leds transition_length: 0s - - output.turn_off: green_led_out # make sure LED is off if no ramp \ No newline at end of file + # (Removed: output.turn_off: green_led_out) # make sure LED is off if no ramp diff --git a/esphome/esp-loungepelmetleds.yaml b/esphome/esp-loungepelmetleds2.yaml similarity index 63% rename from esphome/esp-loungepelmetleds.yaml rename to esphome/esp-loungepelmetleds2.yaml index c41eb30..c4be1b4 100644 --- a/esphome/esp-loungepelmetleds.yaml +++ b/esphome/esp-loungepelmetleds2.yaml @@ -3,12 +3,12 @@ # Title: LOUNGE - PELMET LEDS (H801, 2x Monochrome PWM) # Repo: (new) # -# v2.1-dual - 2025-09-30 Per-channel Min/Max caps moved internal (YAML-only), no HA sliders. +# v2.7 - a bunch of fixes for final version # v2.0-dual - 2025-09-30 Based on single MOSFET version; adapted to H801 dual-monochrome (A/B). # v1.x (history from single-channel base maintained in comments below) # # ------------------------------------------ -# OPERATION (as of v2.0) +# OPERATION (as of v2.x) # ------------------------------------------ # 1. General-purpose LED controller. # 2. Designed here for the H801 5-MOSFET board (ESP8266). Using 2 channels as independent @@ -22,21 +22,23 @@ # 7. (PACKAGES) include common items: network settings, diagnostic entities, MQTT, and SNTP (optional). # 8. Default behaviours, for recovery from lost power or reset: # - "Fade up to full": fade from floor to max on boot. -# - "Restore Brightness": fade from floor to the last non-zero brightness on boot (should be no on/off blip/flash). +# - "Restore Brightness": fade from floor to the last non-zero brightness on boot (no on/off blip/flash). # - "Remain Off": stays off on boot. -# 9. The fading LEDs flash while fading (different patterns for up/down). Channel A uses the **RED** LED on GPIO5. -# Channel B uses the **GREEN** LED on GPIO1 (UART TX; logger baud=0 to free it). +# 9. The board LEDs show output on status. Channel A uses the **RED** LED on GPIO5. +# Channel B uses the **GREEN** LED on GPIO1 (UART TX; logger baud=0) # 10. Fade timing scales with the configured values (proportionally when starting mid-brightness). # 11. Exposed in Home Assistant/MQTT (duplicated for A and B): # - Startup action (On,Off,Restore) -# - Fade Up / Fade Down / Fade Stop buttons # - Fade Up/Down toggle switch # - Normal On/Off switch (quick ramp up/down) # - Fade up/down times (0-60s) -# - Output % (pre-gamma) and PWM % (post-gamma) +# - Output % (pre-gamma) # - Output Set (1-100, respects min/max). This also changes with other output so reflects value. # - Many device diagnostics (from the included 'diagnostics' package) # - Maximum 'on' time before automatic fade-down (1-48 h, 0 = no limit) +# 12. The Fade Up/Down switch auto-syncs to brightness (no ramp is triggered by the sync itself): +# - Stops <33% of cap → switch shows OFF +# - Stops >66% of cap → switch shows ON ########################################################################################### # Hardware: H801 (ESP8266, 5 MOSFET outputs) # ------------------------------------------ @@ -45,8 +47,8 @@ # GPIO02 Blue Status LED, active-low (ESPHOME status) # GPIO12 MOSFET output A (PWM) # GPIO13 MOSFET output B (PWM) -# GPIO05 Red LED (used to display fading status for Channel A) -# GPIO01 Green LED (used to display fading status for Channel B) [UART TX, logger baud=0] +# GPIO05 Red LED (used to display status for Channel A) +# GPIO01 Green LED (used to display status for Channel B) [UART TX, logger baud=0] # ########################################################################################## ########################################################################################## @@ -57,37 +59,25 @@ ########################################################################################## substitutions: # Device Naming - device_name: "esp-loungepelmetleds" # yaml file should be device_name.yaml + device_name: "esp-loungepelmetleds2" # yaml file should be device_name.yaml friendly_name: "Lounge Pelmet LEDs" description_comment: "Lounge Pelmet LEDs :: H801 dual-channel monochrome" device_area: "Lounge" # Allows the ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "H801.5xMOSFET" # Project details. Manufacturer before the dot, device after the dot. - project_version: "v2.1" # Project version denotes release of the YAML file, allowing checking of deployed vs latest version + project_name: "H801.5xMOSFET" # Manufacturer before the dot, device after the dot. + project_version: "v2.7" # Project version denotes release of the YAML file # Passwords & Secrets (Unfortunately, you can't use substitutions inside secret names) api_key: !secret esp-api_key ota_pass: !secret esp-ota_pass - static_ip_address: !secret esp-loungepelmetleds_ip # CHANGE THIS + static_ip_address: !secret esp-loungepelmetleds2_ip # CHANGE THIS - # MQTT LOCAL Controls - mqtt_local_device_name_A: "lounge-pelmetleds_A" - mqtt_local_device_name_B: "lounge-pelmetleds_B" - mqtt_local_command_main_topic: !secret mqtt_local_command_main_topic - mqtt_local_status_main_topic: !secret mqtt_local_status_main_topic - mqtt_local_command_topic_A: "${mqtt_local_command_main_topic}/${mqtt_local_device_name_A}" - mqtt_local_status_topic_A: "${mqtt_local_status_main_topic}/${mqtt_local_device_name_A}" - mqtt_local_command_topic_B: "${mqtt_local_command_main_topic}/${mqtt_local_device_name_B}" - mqtt_local_status_topic_B: "${mqtt_local_status_main_topic}/${mqtt_local_device_name_B}" - mqtt_local_device_command_ON: "ON" - mqtt_local_device_command_OFF: "OFF" - # Device Specific Settings (shared defaults applied per channel below) - log_level: "NONE" - update_interval: "20s" - led_gamma: "1.2" - pwm_frequency: "1000hz" + log_level: "NONE" # Use INFO while testing; flip back to NONE later + update_interval: "60s" + led_gamma: "1.6" + pwm_frequency: "1000Hz" max_on_default_hours: "24" # Per-channel output caps (%), internal-only (no HA sliders) @@ -97,8 +87,8 @@ substitutions: maximum_led_output_B: "90" # Device Specific GPIO - device_status_led: GPIO02 - device_mosfet_out_A: GPIO12 + #device_status_led: GPIO05 + device_mosfet_out_A: GPIO15 device_mosfet_out_B: GPIO13 device_fading_led_A: GPIO05 device_fading_led_B: GPIO01 @@ -115,22 +105,21 @@ packages: local_ota_pass: "${ota_pass}" common_api: !include - #file: common/api_common.yaml file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" - common_mqtt: !include - file: common/mqtt_common.yaml - vars: - local_device_name: "${device_name}" + #common_mqtt: !include + # file: common/mqtt_common.yaml + # vars: + # local_device_name: "${device_name}" #common_webportal: !include common/webportal_common.yaml #common_sntp: !include common/sntp_common.yaml diag_basic: !include common/include_basic_diag_sensors.yaml - #diag_more: !include common/include_more_diag_sensors.yaml - #diag_debug: !include common/include_debug_diag_sensors.yaml + diag_more: !include common/include_more_diag_sensors.yaml + diag_debug: !include common/include_debug_diag_sensors.yaml #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml ########################################################################################## @@ -190,10 +179,10 @@ esp8266: restore_from_flash: true mdns: - disabled: false + disabled: true preferences: - flash_write_interval: 5sec + flash_write_interval: 5min # reduce flash churn while testing ########################################################################################## # GLOBAL VARIABLES @@ -245,7 +234,7 @@ globals: - id: restart_mode type: int restore_value: true - initial_value: '0' + initial_value: '1' # align with select initial_option "Restore Brightness" # Per-channel runtime state (A) - id: ramp_switch_target_on_A @@ -256,6 +245,10 @@ globals: type: bool restore_value: false initial_value: 'false' + - id: suppress_quick_on_A # guard to prevent quick-to-cap on slider turn-on + type: bool + restore_value: false + initial_value: 'false' - id: last_brightness_pct_A type: float restore_value: true @@ -265,7 +258,7 @@ globals: restore_value: false initial_value: '-1' - id: last_ramp_ms_A - type: int + type: uint32_t restore_value: false initial_value: '0' - id: last_nonzero_brightness_pct_A @@ -298,6 +291,10 @@ globals: type: bool restore_value: false initial_value: 'false' + - id: suppress_quick_on_B # guard to prevent quick-to-cap on slider turn-on + type: bool + restore_value: false + initial_value: 'false' - id: last_brightness_pct_B type: float restore_value: true @@ -307,7 +304,7 @@ globals: restore_value: false initial_value: '-1' - id: last_ramp_ms_B - type: int + type: uint32_t restore_value: false initial_value: '0' - id: last_nonzero_brightness_pct_B @@ -337,6 +334,12 @@ globals: restore_value: false initial_value: 'true' + # Compile time Gamma (cached; currently unused since PWM% sensors were removed) + - id: led_gamma_f + type: float + restore_value: false + initial_value: ${led_gamma} + ########################################################################################## # LOGGER ########################################################################################## @@ -355,25 +358,26 @@ logger: ########################################################################################## # MQTT COMMANDS ########################################################################################## -mqtt: - on_message: - - topic: "${mqtt_local_command_topic_A}/lightA/set" - payload: "${mqtt_local_device_command_ON}" - then: - - switch.turn_on: mosfet_ramp_switch_a - - topic: "${mqtt_local_command_topic_A}/lightA/set" - payload: "${mqtt_local_device_command_OFF}" - then: - - switch.turn_off: mosfet_ramp_switch_a - - - topic: "${mqtt_local_command_topic_B}/lightB/set" - payload: "${mqtt_local_device_command_ON}" - then: - - switch.turn_on: mosfet_ramp_switch_b - - topic: "${mqtt_local_command_topic_B}/lightB/set" - payload: "${mqtt_local_device_command_OFF}" - then: - - switch.turn_off: mosfet_ramp_switch_b +#mqtt: + # on_message handlers commented for efficiency (S8). Uncomment if you need local MQTT control again. + #on_message: + # - topic: "${mqtt_local_command_topic_A}/lightA/set" + # payload: "${mqtt_local_device_command_ON}" + # then: + # - switch.turn_on: mosfet_ramp_switch_a + # - topic: "${mqtt_local_command_topic_A}/lightA/set"} + # payload: "${mqtt_local_device_command_OFF}" + # then: + # - switch.turn_off: mosfet_ramp_switch_a + # + # - topic: "${mqtt_local_command_topic_B}/lightB/set" + # payload: "${mqtt_local_device_command_ON}" + # then: + # - switch.turn_on: mosfet_ramp_switch_b + # - topic: "${mqtt_local_command_topic_B}/lightB/set" + # payload: "${mqtt_local_device_command_OFF}" + # then: + # - switch.turn_off: mosfet_ramp_switch_b ########################################################################################## # SWITCHES @@ -452,7 +456,8 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 250ms + update_interval: 200ms # show real-time ramp + disabled_by_default: true lambda: |- const auto &cv = id(mosfet_leds_A).current_values; return cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f; @@ -461,6 +466,7 @@ sensor: - lambda: |- const auto &cv_now = id(mosfet_leds_A).current_values; if (cv_now.is_on()) { + id(last_brightness_pct_A) = x; if (x > 0.5f) { if (!id(is_ramping_A)) id(last_nonzero_brightness_pct_A) = x; id(last_nonzero_tmp_A) = x; @@ -482,24 +488,26 @@ sensor: id(led_output_set_pct_A).publish_state(pos_i); } } - - # Channel A: Output PWM % (post-gamma approx) - - platform: template - id: mosfet_output_pwm_pct_A - name: "${friendly_name} A Output PWM (%)" - unit_of_measurement: "%" - icon: mdi:square-wave - accuracy_decimals: 1 - update_interval: 250ms - lambda: |- - const auto &cv = id(mosfet_leds_A).current_values; - if (!cv.is_on()) return 0.0f; - const float lin = cv.get_brightness(); // 0..1 linear brightness - const float gamma = (float) atof("${led_gamma}"); - float pwm = powf(lin, gamma); - if (pwm < 0.0f) pwm = 0.0f; - if (pwm > 1.0f) pwm = 1.0f; - return pwm * 100.0f; + // Auto-sync fade switch to brightness thresholds (no scripts) + if (!id(booting) && !id(is_ramping_A)) { + float cap = (float) id(max_brightness_pct_A); // cap in % + float low_th = cap * 0.33f; + float high_th = cap * 0.66f; + if (cv_now.is_on()) { + float b = x; // current brightness in % + if (b < low_th - 0.25f) { // hysteresis margin + if (id(ramp_switch_target_on_A)) { + id(ramp_switch_target_on_A) = false; + id(mosfet_ramp_switch_a).publish_state(false); // UI-only; no ramp + } + } else if (b > high_th + 0.25f) { + if (!id(ramp_switch_target_on_A)) { + id(ramp_switch_target_on_A) = true; + id(mosfet_ramp_switch_a).publish_state(true); // UI-only; no ramp + } + } + } + } # Channel B: Output % (pre-gamma) - platform: template @@ -508,7 +516,8 @@ sensor: unit_of_measurement: "%" icon: mdi:percent accuracy_decimals: 0 - update_interval: 250ms + update_interval: 200ms + disabled_by_default: true lambda: |- const auto &cv = id(mosfet_leds_B).current_values; return cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f; @@ -517,6 +526,7 @@ sensor: - lambda: |- const auto &cv_now = id(mosfet_leds_B).current_values; if (cv_now.is_on()) { + id(last_brightness_pct_B) = x; if (x > 0.5f) { if (!id(is_ramping_B)) id(last_nonzero_brightness_pct_B) = x; id(last_nonzero_tmp_B) = x; @@ -538,24 +548,26 @@ sensor: id(led_output_set_pct_B).publish_state(pos_i); } } - - # Channel B: Output PWM % (post-gamma approx) - - platform: template - id: mosfet_output_pwm_pct_B - name: "${friendly_name} B Output PWM (%)" - unit_of_measurement: "%" - icon: mdi:square-wave - accuracy_decimals: 1 - update_interval: 250ms - lambda: |- - const auto &cv = id(mosfet_leds_B).current_values; - if (!cv.is_on()) return 0.0f; - const float lin = cv.get_brightness(); - const float gamma = (float) atof("${led_gamma}"); - float pwm = powf(lin, gamma); - if (pwm < 0.0f) pwm = 0.0f; - if (pwm > 1.0f) pwm = 1.0f; - return pwm * 100.0f; + // Auto-sync fade switch to brightness thresholds (no scripts) + if (!id(booting) && !id(is_ramping_B)) { + float cap = (float) id(max_brightness_pct_B); + float low_th = cap * 0.33f; + float high_th = cap * 0.66f; + if (cv_now.is_on()) { + float b = x; + if (b < low_th - 0.25f) { + if (id(ramp_switch_target_on_B)) { + id(ramp_switch_target_on_B) = false; + id(mosfet_ramp_switch_b).publish_state(false); + } + } else if (b > high_th + 0.25f) { + if (!id(ramp_switch_target_on_B)) { + id(ramp_switch_target_on_B) = true; + id(mosfet_ramp_switch_b).publish_state(true); + } + } + } + } ########################################################################################## # OUTPUTS @@ -590,24 +602,17 @@ light: name: "${friendly_name} A" output: mosfet_pwm_A restore_mode: RESTORE_DEFAULT_OFF - default_transition_length: 2s + default_transition_length: 250ms # quick ramp for normal light toggle icon: mdi:led-strip-variant gamma_correct: "${led_gamma}" on_turn_on: - - mqtt.publish: - topic: "${mqtt_local_status_topic_A}/lightA/state" - payload: "${mqtt_local_device_command_ON}" - retain: true - - lambda: |- - if (!id(ramping_for_off_A)) { - id(ramp_switch_target_on_A) = true; - } - - script.stop: max_on_watchdog_A + # Start/refresh Max-On watchdog quietly whenever light turns on - if: condition: lambda: 'return id(max_on_hours) > 0;' then: + - script.stop: max_on_watchdog_A - script.execute: max_on_watchdog_A on_turn_off: @@ -615,11 +620,6 @@ light: condition: lambda: 'return !id(booting);' then: - - mqtt.publish: - topic: "${mqtt_local_status_topic_A}/lightA/state" - payload: "${mqtt_local_device_command_OFF}" - retain: true - - lambda: 'id(ramp_switch_target_on_A) = false;' - script.stop: max_on_watchdog_A - lambda: |- id(last_brightness_pct_A) = 0.0f; @@ -628,39 +628,39 @@ light: on_state: - lambda: |- + // Mirror board RED LED to this channel's on/off + const auto &cv_s = id(mosfet_leds_A).current_values; + const bool is_on = cv_s.is_on() && (cv_s.get_brightness() > 0.0005f); + if (is_on) id(fading_led_out_A).turn_on(); + else id(fading_led_out_A).turn_off(); + - lambda: |- + // Enforce cap if anyone tries to exceed it const float cap = id(max_brightness_pct_A) / 100.0f; const auto &cv = id(mosfet_leds_A).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds_A).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.002f) return; + auto call = id(mosfet_leds_A).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); - platform: monochromatic id: mosfet_leds_B name: "${friendly_name} B" output: mosfet_pwm_B restore_mode: RESTORE_DEFAULT_OFF - default_transition_length: 2s + default_transition_length: 250ms # quick ramp for normal light toggle icon: mdi:led-strip-variant gamma_correct: "${led_gamma}" on_turn_on: - - mqtt.publish: - topic: "${mqtt_local_status_topic_B}/lightB/state" - payload: "${mqtt_local_device_command_ON}" - retain: true - - lambda: |- - if (!id(ramping_for_off_B)) { - id(ramp_switch_target_on_B) = true; - } - - script.stop: max_on_watchdog_B - if: condition: lambda: 'return id(max_on_hours) > 0;' then: + - script.stop: max_on_watchdog_B - script.execute: max_on_watchdog_B on_turn_off: @@ -668,11 +668,6 @@ light: condition: lambda: 'return !id(booting);' then: - - mqtt.publish: - topic: "${mqtt_local_status_topic_B}/lightB/state" - payload: "${mqtt_local_device_command_OFF}" - retain: true - - lambda: 'id(ramp_switch_target_on_B) = false;' - script.stop: max_on_watchdog_B - lambda: |- id(last_brightness_pct_B) = 0.0f; @@ -680,16 +675,23 @@ light: id(led_output_set_pct_B).publish_state(0); on_state: + - lambda: |- + // Mirror board GREEN LED to this channel's on/off + const auto &cv_s = id(mosfet_leds_B).current_values; + const bool is_on = cv_s.is_on() && (cv_s.get_brightness() > 0.0005f); + if (is_on) id(fading_led_out_B).turn_on(); + else id(fading_led_out_B).turn_off(); - lambda: |- const float cap = id(max_brightness_pct_B) / 100.0f; const auto &cv = id(mosfet_leds_B).current_values; - if (cv.is_on() && cv.get_brightness() > cap + 0.001f) { - auto call = id(mosfet_leds_B).make_call(); - call.set_state(true); - call.set_brightness(cap); - call.set_transition_length(0); - call.perform(); - } + if (!cv.is_on()) return; + const float b = cv.get_brightness(); + if (b <= cap + 0.002f) return; + auto call = id(mosfet_leds_B).make_call(); + call.set_state(true); + call.set_brightness(cap); + call.set_transition_length(0); + call.perform(); ########################################################################################## # NUMBERS @@ -807,7 +809,6 @@ number: id: mosfet_leds_A transition_length: 200ms - lambda: |- - id(ramp_switch_target_on_A) = false; id(led_output_set_pct_A).publish_state(0); id(last_brightness_pct_A) = 0.0f; id(last_set_pos_A) = 0; @@ -816,37 +817,29 @@ number: else: - lambda: |- id(suppress_slider_sync_A) = true; - float pos = x; // 0..100 - if (pos < 1.0f) pos = 1.0f; // 0 is OFF + float pos = x; // 0..100 + if (pos < 1.0f) pos = 1.0f; // 0 is OFF if (pos > 100.0f) pos = 100.0f; id(led_output_set_pct_A).publish_state((int) floorf(pos + 0.5f)); - - script.stop: ramp_off_script_A - - script.stop: ramp_on_script_A - - light.turn_on: - id: mosfet_leds_A - brightness: !lambda |- - float pos = id(led_output_set_pct_A).state; // 1..100 - float minp = (float) id(min_brightness_pct_A); - float maxp = (float) id(max_brightness_pct_A); - if (maxp <= minp) maxp = minp + 1.0f; - float out_pct = minp + (pos * (maxp - minp) / 100.0f); - if (out_pct > maxp) out_pct = maxp; - return out_pct / 100.0f; - transition_length: 250ms - - lambda: |- - id(ramp_switch_target_on_A) = true; - float pos = id(led_output_set_pct_A).state; // 1..100 + + // Map slider position into absolute % within [min..max] float minp = (float) id(min_brightness_pct_A); float maxp = (float) id(max_brightness_pct_A); if (maxp <= minp) maxp = minp + 1.0f; float out_pct = minp + (pos * (maxp - minp) / 100.0f); if (out_pct > maxp) out_pct = maxp; - id(last_brightness_pct_A) = out_pct; - if (out_pct > 0.5f) { - id(last_nonzero_brightness_pct_A) = out_pct; - id(last_nonzero_tmp_A) = out_pct; - } - - delay: 400ms + + // Stash target for the ramp script + id(restore_target_pct_A) = out_pct; + + // Choose duration by direction (up vs down) + const auto &cv = id(mosfet_leds_A).current_values; + float curr = cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f; + id(last_ramp_ms_A) = (out_pct > curr) ? id(ramp_up_ms_A) : id(ramp_down_ms_A); + - script.stop: ramp_off_script_A + - script.stop: ramp_on_script_A + - script.execute: ramp_to_target_script_A + - delay: !lambda 'return (uint32_t) id(last_ramp_ms_A);' - lambda: 'id(suppress_slider_sync_A) = false;' - platform: template @@ -880,7 +873,6 @@ number: id: mosfet_leds_B transition_length: 200ms - lambda: |- - id(ramp_switch_target_on_B) = false; id(led_output_set_pct_B).publish_state(0); id(last_brightness_pct_B) = 0.0f; id(last_set_pos_B) = 0; @@ -889,40 +881,33 @@ number: else: - lambda: |- id(suppress_slider_sync_B) = true; - float pos = x; // 0..100 - if (pos < 1.0f) pos = 1.0f; // 0 is OFF + float pos = x; // 0..100 + if (pos < 1.0f) pos = 1.0f; // 0 is OFF if (pos > 100.0f) pos = 100.0f; id(led_output_set_pct_B).publish_state((int) floorf(pos + 0.5f)); - - script.stop: ramp_off_script_B - - script.stop: ramp_on_script_B - - light.turn_on: - id: mosfet_leds_B - brightness: !lambda |- - float pos = id(led_output_set_pct_B).state; // 1..100 - float minp = (float) id(min_brightness_pct_B); - float maxp = (float) id(max_brightness_pct_B); - if (maxp <= minp) maxp = minp + 1.0f; - float out_pct = minp + (pos * (maxp - minp) / 100.0f); - if (out_pct > maxp) out_pct = maxp; - return out_pct / 100.0f; - transition_length: 250ms - - lambda: |- - id(ramp_switch_target_on_B) = true; - float pos = id(led_output_set_pct_B).state; // 1..100 + + // Map slider position into absolute % within [min..max] float minp = (float) id(min_brightness_pct_B); float maxp = (float) id(max_brightness_pct_B); if (maxp <= minp) maxp = minp + 1.0f; float out_pct = minp + (pos * (maxp - minp) / 100.0f); if (out_pct > maxp) out_pct = maxp; - id(last_brightness_pct_B) = out_pct; // persist exact target now - if (out_pct > 0.5f) { - id(last_nonzero_brightness_pct_B) = out_pct; - id(last_nonzero_tmp_B) = out_pct; - } - - delay: 400ms + + // Stash target for the ramp script + id(restore_target_pct_B) = out_pct; + + // Choose duration by direction (up vs down) + const auto &cv = id(mosfet_leds_B).current_values; + float curr = cv.is_on() ? (cv.get_brightness() * 100.0f) : 0.0f; + id(last_ramp_ms_B) = (out_pct > curr) ? id(ramp_up_ms_B) : id(ramp_down_ms_B); + - script.stop: ramp_off_script_B + - script.stop: ramp_on_script_B + - script.execute: ramp_to_target_script_B + - delay: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - lambda: 'id(suppress_slider_sync_B) = false;' - # Shared "Max On" hours + + # Shared "Max On" hours (0=disabled). When light is turned on, watchdog restarts. - platform: template id: cfg_max_on_hours name: "${friendly_name} Max On (h)" @@ -942,6 +927,7 @@ number: if (hrs > 48) hrs = 48; id(max_on_hours) = hrs; id(cfg_max_on_hours).publish_state((float) hrs); + # Restart/stop watchdogs appropriately for both channels - if: condition: lambda: 'return id(mosfet_leds_A).current_values.is_on();' @@ -967,106 +953,18 @@ number: # SCRIPTS ########################################################################################## script: - # Blink patterns while ramping (A) - - id: led_flash_up_A - mode: restart - then: - - while: - condition: - lambda: 'return true;' - then: - - output.turn_on: fading_led_out_A - - delay: 100ms - - output.turn_off: fading_led_out_A - - delay: 100ms - - output.turn_on: fading_led_out_A - - delay: 100ms - - output.turn_off: fading_led_out_A - - delay: 400ms - - - id: led_flash_down_A - mode: restart - then: - - while: - condition: - lambda: 'return true;' - then: - - output.turn_on: fading_led_out_A - - delay: 250ms - - output.turn_off: fading_led_out_A - - delay: 250ms - - # Blink patterns while ramping (B) - - id: led_flash_up_B - mode: restart - then: - - while: - condition: - lambda: 'return true;' - then: - - output.turn_on: fading_led_out_B - - delay: 100ms - - output.turn_off: fading_led_out_B - - delay: 100ms - - output.turn_on: fading_led_out_B - - delay: 100ms - - output.turn_off: fading_led_out_B - - delay: 400ms - - - id: led_flash_down_B - mode: restart - then: - - while: - condition: - lambda: 'return true;' - then: - - output.turn_on: fading_led_out_B - - delay: 250ms - - output.turn_off: fading_led_out_B - - delay: 250ms - # Ramps: Channel A - id: ramp_on_script_A mode: restart then: - lambda: 'id(is_ramping_A) = true;' - script.stop: ramp_off_script_A - - script.stop: led_flash_down_A - - script.execute: led_flash_up_A - - if: - condition: - lambda: |- - const auto &cv = id(mosfet_leds_A).current_values; - const float floor = id(min_brightness_pct_A) / 100.0f; - return (!cv.is_on()) || (cv.get_brightness() + 0.0005f < floor); - then: - - light.turn_on: - id: mosfet_leds_A - brightness: !lambda 'return id(min_brightness_pct_A) / 100.0f;' - transition_length: 80ms + # Single transition to CAP for the full configured time - light.turn_on: id: mosfet_leds_A brightness: !lambda 'return id(max_brightness_pct_A) / 100.0f;' - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_A).current_values; - const float floor = id(min_brightness_pct_A) / 100.0f; - const float cap = id(max_brightness_pct_A) / 100.0f; - if (cap <= floor + 0.0005f) { id(last_ramp_ms_A) = 0; return (uint32_t) 0; } - float curr = cv.is_on() ? cv.get_brightness() : 0.0f; - if (curr + 0.0005f < floor) { - id(last_ramp_ms_A) = id(ramp_up_ms_A); - return (uint32_t) id(last_ramp_ms_A); - } - if (curr > cap) curr = cap; - if (curr < floor) curr = floor; - float frac = (cap - curr) / (cap - floor); - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_A) = (int) (id(ramp_up_ms_A) * frac); - return (uint32_t) id(last_ramp_ms_A); - - delay: !lambda 'return (uint32_t) id(last_ramp_ms_A);' - - script.stop: led_flash_up_A - - output.turn_off: fading_led_out_A + transition_length: !lambda 'return (uint32_t) id(ramp_up_ms_A);' + - delay: !lambda 'return (uint32_t) id(ramp_up_ms_A);' - lambda: |- id(is_ramping_A) = false; const auto &cv = id(mosfet_leds_A).current_values; @@ -1081,53 +979,16 @@ script: then: - lambda: 'id(is_ramping_A) = true;' - script.stop: ramp_off_script_A - - script.stop: led_flash_down_A - - script.execute: led_flash_up_A - - if: - condition: - lambda: |- - const auto &cv = id(mosfet_leds_A).current_values; - const float floor = id(min_brightness_pct_A) / 100.0f; - return (!cv.is_on()) || (cv.get_brightness() + 0.0005f < floor); - then: - - light.turn_on: - id: mosfet_leds_A - brightness: !lambda 'return id(min_brightness_pct_A) / 100.0f;' - transition_length: 80ms - light.turn_on: id: mosfet_leds_A - brightness: !lambda |- - float cap_pct = id(restore_target_pct_A); - float maxp = (float) id(max_brightness_pct_A); - if (cap_pct > maxp) cap_pct = maxp; - return cap_pct / 100.0f; - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_A).current_values; - const float floor = id(min_brightness_pct_A) / 100.0f; - float cap = id(restore_target_pct_A) / 100.0f; - const float maxp = id(max_brightness_pct_A) / 100.0f; - if (cap > maxp) cap = maxp; - if (cap <= floor + 0.0005f) { id(last_ramp_ms_A) = 0; return (uint32_t) 0; } - float curr = cv.is_on() ? cv.get_brightness() : 0.0f; - if (curr + 0.0005f < floor) { - id(last_ramp_ms_A) = id(ramp_up_ms_A); - return (uint32_t) id(last_ramp_ms_A); - } - if (curr > cap) curr = cap; - if (curr < floor) curr = floor; - float frac = (cap - curr) / (cap - floor); - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_A) = (int) (id(ramp_up_ms_A) * frac); - return (uint32_t) id(last_ramp_ms_A); + brightness: !lambda 'return id(restore_target_pct_A) / 100.0f;' + transition_length: !lambda 'return (uint32_t) id(last_ramp_ms_A);' - delay: !lambda 'return (uint32_t) id(last_ramp_ms_A);' - - script.stop: led_flash_up_A - - output.turn_off: fading_led_out_A - lambda: |- id(is_ramping_A) = false; - const auto &cv = id(mosfet_leds_A).current_values; - if (cv.is_on()) { - float pct = cv.get_brightness() * 100.0f; + const auto &cv2 = id(mosfet_leds_A).current_values; + if (cv2.is_on()) { + float pct = cv2.get_brightness() * 100.0f; id(last_brightness_pct_A) = pct; if (pct > 0.5f) id(last_nonzero_brightness_pct_A) = pct; } @@ -1139,30 +1000,11 @@ script: id(is_ramping_A) = true; id(ramping_for_off_A) = true; - script.stop: ramp_on_script_A - - script.stop: led_flash_up_A - - script.execute: led_flash_down_A - - light.turn_on: - id: mosfet_leds_A - brightness: !lambda 'return id(min_brightness_pct_A) / 100.0f;' - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_A).current_values; - const float floor = id(min_brightness_pct_A) / 100.0f; - float curr = cv.get_brightness(); - if (curr < floor) curr = floor; - const float denom = (1.0f - floor); - if (denom <= 0.0005f) { id(last_ramp_ms_A) = 0; return (uint32_t) 0; } - float frac = (curr - floor) / denom; - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_A) = (int) (id(ramp_down_ms_A) * frac); - return (uint32_t) id(last_ramp_ms_A); - - delay: !lambda 'return (uint32_t) id(last_ramp_ms_A);' + # Single transition directly to OFF for the full configured time - light.turn_off: id: mosfet_leds_A - transition_length: 150ms - - delay: 150ms - - script.stop: led_flash_down_A - - output.turn_off: fading_led_out_A + transition_length: !lambda 'return (uint32_t) id(ramp_down_ms_A);' + - delay: !lambda 'return (uint32_t) id(ramp_down_ms_A);' - lambda: |- id(is_ramping_A) = false; id(ramping_for_off_A) = false; @@ -1193,12 +1035,13 @@ script: then: - delay: 5s - lambda: |- - float target = id(last_brightness_pct_A); + float target = id(last_nonzero_brightness_pct_A); float minp = (float) id(min_brightness_pct_A); float maxp = (float) id(max_brightness_pct_A); if (target > 0.0f && target < minp) target = minp; if (target > maxp) target = maxp; id(restore_target_pct_A) = target; + id(last_ramp_ms_A) = id(ramp_up_ms_A); - if: condition: lambda: 'return id(restore_target_pct_A) > 0.5f;' @@ -1206,10 +1049,13 @@ script: - lambda: |- id(ramp_switch_target_on_A) = true; id(suppress_slider_sync_A) = true; + id(suppress_quick_on_A) = true; - script.stop: ramp_off_script_A - script.execute: ramp_to_target_script_A - delay: !lambda 'return (uint32_t) id(last_ramp_ms_A);' - - lambda: 'id(suppress_slider_sync_A) = false;' + - lambda: |- + id(suppress_slider_sync_A) = false; + id(suppress_quick_on_A) = false; else: - lambda: 'id(ramp_switch_target_on_A) = false;' - light.turn_off: @@ -1222,42 +1068,11 @@ script: then: - lambda: 'id(is_ramping_B) = true;' - script.stop: ramp_off_script_B - - script.stop: led_flash_down_B - - script.execute: led_flash_up_B - - if: - condition: - lambda: |- - const auto &cv = id(mosfet_leds_B).current_values; - const float floor = id(min_brightness_pct_B) / 100.0f; - return (!cv.is_on()) || (cv.get_brightness() + 0.0005f < floor); - then: - - light.turn_on: - id: mosfet_leds_B - brightness: !lambda 'return id(min_brightness_pct_B) / 100.0f;' - transition_length: 80ms - light.turn_on: id: mosfet_leds_B brightness: !lambda 'return id(max_brightness_pct_B) / 100.0f;' - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_B).current_values; - const float floor = id(min_brightness_pct_B) / 100.0f; - const float cap = id(max_brightness_pct_B) / 100.0f; - if (cap <= floor + 0.0005f) { id(last_ramp_ms_B) = 0; return (uint32_t) 0; } - float curr = cv.is_on() ? cv.get_brightness() : 0.0f; - if (curr + 0.0005f < floor) { - id(last_ramp_ms_B) = id(ramp_up_ms_B); - return (uint32_t) id(last_ramp_ms_B); - } - if (curr > cap) curr = cap; - if (curr < floor) curr = floor; - float frac = (cap - curr) / (cap - floor); - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_B) = (int) (id(ramp_up_ms_B) * frac); - return (uint32_t) id(last_ramp_ms_B); - - delay: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - - script.stop: led_flash_up_B - - output.turn_off: fading_led_out_B + transition_length: !lambda 'return (uint32_t) id(ramp_up_ms_B);' + - delay: !lambda 'return (uint32_t) id(ramp_up_ms_B);' - lambda: |- id(is_ramping_B) = false; const auto &cv = id(mosfet_leds_B).current_values; @@ -1272,53 +1087,16 @@ script: then: - lambda: 'id(is_ramping_B) = true;' - script.stop: ramp_off_script_B - - script.stop: led_flash_down_B - - script.execute: led_flash_up_B - - if: - condition: - lambda: |- - const auto &cv = id(mosfet_leds_B).current_values; - const float floor = id(min_brightness_pct_B) / 100.0f; - return (!cv.is_on()) || (cv.get_brightness() + 0.0005f < floor); - then: - - light.turn_on: - id: mosfet_leds_B - brightness: !lambda 'return id(min_brightness_pct_B) / 100.0f;' - transition_length: 80ms - light.turn_on: id: mosfet_leds_B - brightness: !lambda |- - float cap_pct = id(restore_target_pct_B); - float maxp = (float) id(max_brightness_pct_B); - if (cap_pct > maxp) cap_pct = maxp; - return cap_pct / 100.0f; - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_B).current_values; - const float floor = id(min_brightness_pct_B) / 100.0f; - float cap = id(restore_target_pct_B) / 100.0f; - const float maxp = id(max_brightness_pct_B) / 100.0f; - if (cap > maxp) cap = maxp; - if (cap <= floor + 0.0005f) { id(last_ramp_ms_B) = 0; return (uint32_t) 0; } - float curr = cv.is_on() ? cv.get_brightness() : 0.0f; - if (curr + 0.0005f < floor) { - id(last_ramp_ms_B) = id(ramp_up_ms_B); - return (uint32_t) id(last_ramp_ms_B); - } - if (curr > cap) curr = cap; - if (curr < floor) curr = floor; - float frac = (cap - curr) / (cap - floor); - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_B) = (int) (id(ramp_up_ms_B) * frac); - return (uint32_t) id(last_ramp_ms_B); + brightness: !lambda 'return id(restore_target_pct_B) / 100.0f;' + transition_length: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - delay: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - - script.stop: led_flash_up_B - - output.turn_off: fading_led_out_B - lambda: |- id(is_ramping_B) = false; - const auto &cv = id(mosfet_leds_B).current_values; - if (cv.is_on()) { - float pct = cv.get_brightness() * 100.0f; + const auto &cv2 = id(mosfet_leds_B).current_values; + if (cv2.is_on()) { + float pct = cv2.get_brightness() * 100.0f; id(last_brightness_pct_B) = pct; if (pct > 0.5f) id(last_nonzero_brightness_pct_B) = pct; } @@ -1330,30 +1108,10 @@ script: id(is_ramping_B) = true; id(ramping_for_off_B) = true; - script.stop: ramp_on_script_B - - script.stop: led_flash_up_B - - script.execute: led_flash_down_B - - light.turn_on: - id: mosfet_leds_B - brightness: !lambda 'return id(min_brightness_pct_B) / 100.0f;' - transition_length: !lambda |- - const auto &cv = id(mosfet_leds_B).current_values; - const float floor = id(min_brightness_pct_B) / 100.0f; - float curr = cv.get_brightness(); - if (curr < floor) curr = floor; - const float denom = (1.0f - floor); - if (denom <= 0.0005f) { id(last_ramp_ms_B) = 0; return (uint32_t) 0; } - float frac = (curr - floor) / denom; - if (frac < 0.0f) frac = 0.0f; - if (frac > 1.0f) frac = 1.0f; - id(last_ramp_ms_B) = (int) (id(ramp_down_ms_B) * frac); - return (uint32_t) id(last_ramp_ms_B); - - delay: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - light.turn_off: id: mosfet_leds_B - transition_length: 150ms - - delay: 150ms - - script.stop: led_flash_down_B - - output.turn_off: fading_led_out_B + transition_length: !lambda 'return (uint32_t) id(ramp_down_ms_B);' + - delay: !lambda 'return (uint32_t) id(ramp_down_ms_B);' - lambda: |- id(is_ramping_B) = false; id(ramping_for_off_B) = false; @@ -1384,12 +1142,13 @@ script: then: - delay: 5s - lambda: |- - float target = id(last_brightness_pct_B); + float target = id(last_nonzero_brightness_pct_B); float minp = (float) id(min_brightness_pct_B); float maxp = (float) id(max_brightness_pct_B); if (target > 0.0f && target < minp) target = minp; if (target > maxp) target = maxp; id(restore_target_pct_B) = target; + id(last_ramp_ms_B) = id(ramp_up_ms_B); - if: condition: lambda: 'return id(restore_target_pct_B) > 0.5f;' @@ -1397,12 +1156,17 @@ script: - lambda: |- id(ramp_switch_target_on_B) = true; id(suppress_slider_sync_B) = true; + id(suppress_quick_on_B) = true; - script.stop: ramp_off_script_B - script.execute: ramp_to_target_script_B - delay: !lambda 'return (uint32_t) id(last_ramp_ms_B);' - - lambda: 'id(suppress_slider_sync_B) = false;' + - lambda: |- + id(suppress_slider_sync_B) = false; + id(suppress_quick_on_B) = false; else: - lambda: 'id(ramp_switch_target_on_B) = false;' - light.turn_off: id: mosfet_leds_B transition_length: 0s +########################################################################################## +########################################################################################## diff --git a/esphome/esp-loungesouthleftswitch.yaml b/esphome/esp-loungesouthleftswitch.yaml index 862281f..ec104aa 100644 --- a/esphome/esp-loungesouthleftswitch.yaml +++ b/esphome/esp-loungesouthleftswitch.yaml @@ -30,18 +30,20 @@ substitutions: api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names static_ip_address: !secret esp-loungesouthleftswitch_ip - #mqtt_local_command_main_topic: !secret mqtt_local_command_main_topic - #mqtt_local_status_main_topic: !secret mqtt_local_status_main_topic + mqtt_command_main_topic: !secret mqtt_command_main_topic + mqtt_status_main_topic: !secret mqtt_status_main_topic # Device Settings #relay_icon: "mdi:lightbulb-group" log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for for general sensors etc - # MQTT LOCAL Controls - #mqtt_device_name: "bedroom2-lights" - #mqtt_local_command_topic: "${mqtt_local_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA - #mqtt_local_status_topic: "${mqtt_local_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA + # MQTT REMOTE Controls + mqtt_remote_device_name: "garage-corridor-lights" + mqtt_remote_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device_name}" + mqtt_remote_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device_name}" + mqtt_remote_device_command_ON: "ON" + mqtt_remote_device_command_OFF: "OFF" # Switch Naming switch_1_name: "Wall Washer Left" @@ -98,10 +100,6 @@ esphome: project: name: "${project_name}" version: "${project_version}" - #on_boot: - # priority: 200 - # then: - # - switch.turn_on: Relay_2 ######################################################################################### # ESP Platform and Framework @@ -122,6 +120,12 @@ logger: #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 +globals: + - id: relay2_change_origin_remote + type: bool + restore_value: no + initial_value: "false" + ######################################################################################### # STATUS LED # https://esphome.io/components/status_led.html @@ -154,15 +158,38 @@ binary_sensor: on_press: - switch.toggle: Relay_2 -# KS-811-2 is a double only -# - platform: gpio -# pin: -# number: GPIO4 -# mode: INPUT -# inverted: True -# name: "Button 3: ${switch_3_name}" -# on_press: -# - switch.toggle: Relay_3 +text_sensor: + - platform: mqtt_subscribe + id: remote2_status + topic: "${mqtt_remote_status_topic}" + on_value: + then: + - if: + condition: + lambda: |- + return x == "${mqtt_remote_device_command_ON}"; + then: + - if: + condition: + not: + switch.is_on: Relay_2 + then: + - lambda: |- + id(relay2_change_origin_remote) = true; + - switch.turn_on: Relay_2 + + - if: + condition: + lambda: |- + return x == "${mqtt_remote_device_command_OFF}"; + then: + - if: + condition: + switch.is_on: Relay_2 + then: + - lambda: |- + id(relay2_change_origin_remote) = true; + - switch.turn_off: Relay_2 ######################################################################################### # SWITCH COMPONENT @@ -178,11 +205,24 @@ switch: name: "Relay 2: ${switch_2_name}" pin: GPIO12 id: Relay_2 - -# KS-811-2 is a double only -# - platform: gpio -# name: "Relay 3: ${switch_3_name}" -# pin: GPIO14 -# id: Relay_3 + on_turn_on: + - if: + condition: + lambda: 'return !id(relay2_change_origin_remote);' + then: + - mqtt.publish: + topic: "${mqtt_remote_command_topic}" + payload: "${mqtt_remote_device_command_ON}" + - lambda: 'id(relay2_change_origin_remote) = false;' + on_turn_off: + - if: + condition: + lambda: 'return !id(relay2_change_origin_remote);' + then: + - mqtt.publish: + topic: "${mqtt_remote_command_topic}" + payload: "${mqtt_remote_device_command_OFF}" + - lambda: 'id(relay2_change_origin_remote) = false;' + diff --git a/esphome/esp-occupancystair.yaml b/esphome/esp-occupancystair.yaml index d2d12bf..f3d36cc 100644 --- a/esphome/esp-occupancystair.yaml +++ b/esphome/esp-occupancystair.yaml @@ -44,7 +44,7 @@ substitutions: # MQTT REMOTE Controls mqtt_remote_device1_name: "stair-footerlights" - mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}/set" + mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}" #mqtt_remote_device1_status_topic: "${mqtt_status_main_topic}/${mqtt_remote_device1_name}/state" mqtt_remote_device_command_ON: "ON" mqtt_remote_device_command_OFF: "OFF" diff --git a/packages/lounge_lighting_scenes.yaml b/packages/lounge_lighting_scenes.yaml index 64d7430..db384b5 100644 --- a/packages/lounge_lighting_scenes.yaml +++ b/packages/lounge_lighting_scenes.yaml @@ -74,107 +74,120 @@ script: ##################################################################### scene_settings: Bright: - light.esp_lounge6chdimmer_couch_spots_left: 00 - light.esp_lounge6chdimmer_couch_spots_right: 00 + light.esp_lounge6chdimmer_couch_spots_left: 0 + light.esp_lounge6chdimmer_couch_spots_right: 0 #light.esp_lounge6chdimmer_dining_table_light: 100 - light.esp_lounge6chdimmer_lounge_downlights_centre: 100 # Centre Downlights (x6, dimmable) - light.esp_lounge6chdimmer_lounge_downlights_south: 100 # South Downlights (x4, dimmable) - light.esp_lounge6chdimmer_lounge_rafter_buttons: 100 # Buttons in Rafters (x3, dimmable) - switch.esp_loungemiddleswitch_relay_3_downlights_north: "on" # North Downlights (x2 over stairs, non dimmable) + light.esp_lounge6chdimmer_lounge_downlights_centre: 100 + light.esp_lounge6chdimmer_lounge_downlights_south: 100 + light.esp_lounge6chdimmer_lounge_rafter_buttons: 100 + switch.esp_loungemiddleswitch_relay_3_downlights_north: "on" light.esp_breakfastbarleds_breakfast_bar_leds: 90 light.esp_loungecabinetleds2_lounge_cabinet_leds: 90 light.esp_loungebookshelfleds_lounge_bookshelf_leds: 90 - light.tasmo_h801_loungeled1_6180_a: 90 - light.tasmo_h801_loungeled1_6180_b: 90 - switch.lounge_cupboard_dual_usb_xus09_l2: on - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: on # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: on # Back wall wallwasher right - switch.tasmo_ks811t_0702_lounge_3c: on # North Downlights (x2 over stairs, non dimmable) - switch.esp_loungenorthswitch_relay_1_main_lights: on # Single switch to hallway relay output (Pelmet LEDs) + switch.lounge_cupboard_dual_usb_xus09_l2: "on" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "on" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "on" + switch.tasmo_ks811t_0702_lounge_3c: "on" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 100 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 100 + + TV General: + light.esp_lounge6chdimmer_couch_spots_left: 60 + light.esp_lounge6chdimmer_couch_spots_right: 60 + #light.esp_lounge6chdimmer_dining_table_light: 100 + light.esp_lounge6chdimmer_lounge_downlights_centre: 60 + light.esp_lounge6chdimmer_lounge_downlights_south: 60 + light.esp_lounge6chdimmer_lounge_rafter_buttons: 40 + light.esp_breakfastbarleds_breakfast_bar_leds: 60 + light.esp_loungecabinetleds2_lounge_cabinet_leds: 60 + light.esp_loungebookshelfleds_lounge_bookshelf_leds: 60 + switch.esp_loungemiddleswitch_relay_3_downlights_north: "off" # non-dimmable pair + switch.lounge_cupboard_dual_usb_xus09_l2: "on" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "off" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "off" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 60 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 60 Movie: - #light.esp_lounge6chdimmer_couch_spots_left: 40 - #light.esp_lounge6chdimmer_couch_spots_right: 40 + light.esp_lounge6chdimmer_couch_spots_left: 20 + light.esp_lounge6chdimmer_couch_spots_right: 20 #light.esp_lounge6chdimmer_dining_table_light: 100 - light.esp_lounge6chdimmer_lounge_downlights_centre: 20 # Centre Downlights (x6, dimmable) - light.esp_lounge6chdimmer_lounge_downlights_south: 20 # South Downlights (x4, dimmable) - light.esp_lounge6chdimmer_lounge_rafter_buttons: 20 # Buttons in Rafters (x3, dimmable) + light.esp_lounge6chdimmer_lounge_downlights_centre: 20 + light.esp_lounge6chdimmer_lounge_downlights_south: 20 + light.esp_lounge6chdimmer_lounge_rafter_buttons: 20 light.esp_breakfastbarleds_breakfast_bar_leds: 40 light.esp_loungecabinetleds2_lounge_cabinet_leds: 40 light.esp_loungebookshelfleds_lounge_bookshelf_leds: 40 - light.tasmo_h801_loungeled1_6180_a: 60 - light.tasmo_h801_loungeled1_6180_b: 60 - switch.esp_loungemiddleswitch_relay_3_downlights_north: off # North Downlights (x2 over stairs, non dimmable) switch.lounge_cupboard_dual_usb_xus09_l2: "on" - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: off # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: off # Back wall wallwasher right - switch.esp_loungenorthswitch_relay_1_main_lights: on # Single switch to hallway relay output (Pelmet LEDs) - - Night Light: - #light.esp_lounge6chdimmer_couch_spots_left: 40 - #light.esp_lounge6chdimmer_couch_spots_right: 40 - #light.esp_lounge6chdimmer_dining_table_light: 100 - light.esp_lounge6chdimmer_lounge_downlights_centre: 5 # Centre Downlights (x6, dimmable) - #light.esp_lounge6chdimmer_lounge_downlights_south: 20 # South Downlights (x4, dimmable) - light.esp_lounge6chdimmer_lounge_rafter_buttons: 5 # Buttons in Rafters (x3, dimmable) - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: off # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: off # Back wall wallwasher right + switch.esp_loungemiddleswitch_relay_3_downlights_north: "off" + switch.lounge_cupboard_dual_usb_xus09_l2: "on" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "off" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "off" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 40 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 40 Reading On/Off: - light.esp_lounge6chdimmer_couch_spots_left: toggle_75 - light.esp_lounge6chdimmer_couch_spots_right: toggle_75 + light.esp_lounge6chdimmer_couch_spots_left: "toggle_75" + light.esp_lounge6chdimmer_couch_spots_right: "toggle_75" #light.esp_lounge6chdimmer_dining_table_light: 60 #light.esp_lounge6chdimmer_lounge_downlights_centre: 45 #light.esp_lounge6chdimmer_lounge_downlights_south: 35 #light.esp_lounge6chdimmer_lounge_rafter_buttons: 20 - #switch.tasmo_ks811t_0702_lounge_3c: on + #switch.tasmo_ks811t_0702_lounge_3c: "on" + + Night Light: + #light.esp_lounge6chdimmer_couch_spots_left: 20 + #light.esp_lounge6chdimmer_couch_spots_right: 20 + #light.esp_lounge6chdimmer_dining_table_light: 100 + light.esp_lounge6chdimmer_lounge_downlights_centre: 0 + light.esp_lounge6chdimmer_lounge_downlights_south: 0 + light.esp_lounge6chdimmer_lounge_rafter_buttons: 0 + light.esp_breakfastbarleds_breakfast_bar_leds: 5 + light.esp_loungecabinetleds2_lounge_cabinet_leds: 5 + light.esp_loungebookshelfleds_lounge_bookshelf_leds: 5 + switch.esp_loungemiddleswitch_relay_3_downlights_north: "off" + switch.lounge_cupboard_dual_usb_xus09_l2: "off" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "off" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "off" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 5 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 5 Warm Cozy: - light.esp_lounge6chdimmer_couch_spots_left: 30 - light.esp_lounge6chdimmer_couch_spots_right: 30 + light.esp_lounge6chdimmer_couch_spots_left: 20 + light.esp_lounge6chdimmer_couch_spots_right: 20 light.esp_lounge6chdimmer_lounge_rafter_buttons: 15 light.esp_breakfastbarleds_breakfast_bar_leds: 25 light.esp_loungecabinetleds2_lounge_cabinet_leds: 30 light.esp_loungebookshelfleds_lounge_bookshelf_leds: 30 light.tasmo_h801_loungeled1_6180_a: 25 light.tasmo_h801_loungeled1_6180_b: 25 - switch.lounge_cupboard_dual_usb_xus09_l2: on - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: on # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: on # Back wall wallwasher right - - TV General: - light.esp_lounge6chdimmer_couch_spots_left: 50 - light.esp_lounge6chdimmer_couch_spots_right: 50 - #light.esp_lounge6chdimmer_dining_table_light: 100 - light.esp_lounge6chdimmer_lounge_downlights_centre: 40 # Centre Downlights (x6, dimmable) - light.esp_lounge6chdimmer_lounge_downlights_south: 40 # South Downlights (x4, dimmable) - light.esp_lounge6chdimmer_lounge_rafter_buttons: 40 # Buttons in Rafters (x3, dimmable) - light.esp_breakfastbarleds_breakfast_bar_leds: 60 - light.esp_loungecabinetleds2_lounge_cabinet_leds: 60 - light.esp_loungebookshelfleds_lounge_bookshelf_leds: 60 - light.tasmo_h801_loungeled1_6180_a: 60 - light.tasmo_h801_loungeled1_6180_b: 60 - switch.esp_loungemiddleswitch_relay_3_downlights_north: off # North Downlights (x2 over stairs, non dimmable) switch.lounge_cupboard_dual_usb_xus09_l2: "on" - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: off # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: off # Back wall wallwasher right - switch.esp_loungenorthswitch_relay_1_main_lights: on # Single switch to hallway relay output (Pelmet LEDs) + switch.lounge_cupboard_dual_usb_xus09_l2: "on" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "on" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "on" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 20 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 20 All Off: light.esp_lounge6chdimmer_couch_spots_left: 0 light.esp_lounge6chdimmer_couch_spots_right: 0 light.esp_lounge6chdimmer_dining_table_light: 0 - light.esp_lounge6chdimmer_lounge_downlights_centre: 0 # Centre Downlights (x6, dimmable) - light.esp_lounge6chdimmer_lounge_downlights_south: 0 # South Downlights (x4, dimmable) - light.esp_lounge6chdimmer_lounge_rafter_buttons: 0 # Buttons in Rafters (x3, dimmable) - switch.esp_loungemiddleswitch_relay_3_downlights_north: "off" # North Downlights (x2 over stairs, non dimmable) + light.esp_lounge6chdimmer_lounge_downlights_centre: 0 + light.esp_lounge6chdimmer_lounge_downlights_south: 0 + light.esp_lounge6chdimmer_lounge_rafter_buttons: 0 + switch.esp_loungemiddleswitch_relay_3_downlights_north: "off" light.esp_breakfastbarleds_breakfast_bar_leds: 0 light.esp_loungecabinetleds2_lounge_cabinet_leds: 0 light.esp_loungebookshelfleds_lounge_bookshelf_leds: 0 - light.tasmo_h801_loungeled1_6180_a: 0 - light.tasmo_h801_loungeled1_6180_b: 0 - switch.lounge_cupboard_dual_usb_xus09_l2: off - switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: off # Back wall wallwasher Left - switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: off # Back wall wallwasher right - switch.esp_loungenorthswitch_relay_1_main_lights: off # Single switch to hallway relay output (Pelmet LEDs) + switch.lounge_cupboard_dual_usb_xus09_l2: "off" + switch.esp_loungesouthleftswitch_relay_1_wall_washer_left: "off" + switch.esp_loungesouthrightswitch_relay_2_wall_washer_right: "off" + #switch.esp_loungenorthswitch_relay_1_main_lights: "on" + number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 0 + number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 0 ##################################################################### ##################################################################### @@ -202,8 +215,17 @@ script: is_toggle: "{{ (not is_num) and (vstr|lower).startswith('toggle') }}" toggle_pct: "{{ (vstr.split('_')|last)|int if ('_' in vstr) else 100 }}" currently_on: "{{ is_state(ent, 'on') }}" + is_number_ent: "{{ ent.startswith('number.') }}" # 🔹 NEW - choose: + # 0) Numbers: set numeric value (e.g. 0..100) + - conditions: "{{ is_number_ent and is_num }}" # 🔹 NEW + sequence: + - service: number.set_value + data: + entity_id: "{{ ent }}" + value: "{{ val|float }}" + # 1) Lights: toggle[_NN] -> if on turn off, else turn on to NN (default 100) - conditions: "{{ is_light and is_toggle }}" sequence: diff --git a/packages/moonraker_3dprinter copy.yaml.bak b/packages/moonraker_3dprinter copy.yaml.bak deleted file mode 100644 index 6f41f4d..0000000 --- a/packages/moonraker_3dprinter copy.yaml.bak +++ /dev/null @@ -1,370 +0,0 @@ -rest: - scan_interval: 5 - resource_template: "http://192.168.2.31:7125/server/files/metadata?filename={{ states(('sensor.3d_printer_current_print')) | urlencode }}" - sensor: - - name: printer_3d_file_metadata - unique_id: "192.168.2.315ec44d90-419c-419c-802d-d34071639c08" - json_attributes_path: "$.result" - json_attributes: - - layer_height - - object_height - - thumbnails - value_template: "OK" - -#camera: -# Loading generic IP camera via configuration.yaml is deprecated, it will be automatically imported. Once you have confirmed correct operation, please remove 'generic' (IP camera) section(s) from configuration.yaml -# Directions in the README.md for how to add the Thumbnail now. -# - platform: generic -# name: "3D Printer Thumbnail" -# still_image_url: http://192.168.2.31:7125/server/files/gcodes/{{ states("sensor.3d_printer_object_thumbnails") }} -# verify_ssl: false - -# This no longer works broken in current builds https://www.home-assistant.io/blog/2022/05/04/release-20225/#breaking-changes -> MJPEG IP Camera -# Directions in the README.md for how to add the MJPEG IP Camera now. -# - platform: mjpeg -# name: "3D Printer Camera" -# still_image_url: http://192.168.2.31/webcam/?action=snapshot -# mjpeg_url: http://192.168.2.31/webcam/?action=stream -# verify_ssl: false - -rest_command: - 3d_printer_emergency_stop: - url: "http://192.168.2.31:7125/printer/emergency_stop" - method: post - 3d_printer_firmware_restart: - url: "http://192.168.2.31:7125/printer/firmware_restart" - method: post - 3d_printer_cancel: - url: "http://192.168.2.31:7125/printer/print/cancel" - method: post - 3d_printer_pause: - url: "http://192.168.2.31:7125/printer/print/pause" - method: post - 3d_printer_resume: - url: "http://192.168.2.31:7125/printer/print/resume" - method: post - -sensor: - - platform: rest - name: printer_3d_sensor - unique_id: "192.168.2.313a0c25fa-297d-4c19-b03c-ddf04840682b" - resource: "http://192.168.2.31:7125/printer/objects/query?heater_bed&extruder&print_stats&toolhead&display_status&virtual_sdcard&gcode_move&filament_motion_sensor%20btt_smart_filament&temperature_sensor%20Chamber_Temp" - json_attributes_path: "$.result.status" - json_attributes: - - heater_bed - - extruder - - print_stats - - toolhead - - display_status - - virtual_sdcard - - gcode_move - - "filament_motion_sensor btt_smart_filament_sensor" - - "temperature_sensor Chamber_Temp" - value_template: "OK" - force_update: true - scan_interval: 1 - - - platform: rest - name: printer_3d_info - unique_id: "192.168.2.311cba6677-02bd-4273-9083-b8301bf6943b" - scan_interval: 1 - resource_template: "http://192.168.2.31:7125/printer/info" - json_attributes_path: "$.result" - json_attributes: - - state_message - - state - value_template: "OK" - - - platform: rest - name: printer_3d_server - unique_id: "192.168.2.311cba6677-02bd-4273-9083-b8301bf6949" - scan_interval: 1 - resource_template: "http://192.168.2.31:7125/server/database/item?namespace=mainsail&key=general" - json_attributes_path: "$.result.value" - json_attributes: - - printername - value_template: "OK" - -template: - - binary_sensor: - - name: 3d_printer_filament - unique_id: "192.168.2.315ce32fc9-5e95-49e7-80e7-bfdd2cf1d1fd" - device_class: motion - state: '{{ states.sensor.printer_3d_sensor.attributes["filament_switch_sensor Filament"]["filament_detected"] }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:printer-3d-nozzle-alert - attributes: - friendly_name: "Filament Detected" - - sensor: - - name: 3d_printer_printername - unique_id: "192.168.2.313a0f3144-a801-422f-adb6-e2ed35796074" - state: '{{ states.sensor.printer_3d_server.attributes["printername"]}}' - availability: > - {% set items = ['sensor.printer_3d_server'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:printer-3d - attributes: - friendly_name: "Printer Name" - - name: 3d_printer_chamber_temp - unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f741" - state: '{{ states.sensor.printer_3d_sensor.attributes["temperature_sensor Chamber_Temp"]["temperature"] | float(0) | round(1) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "°C" - icon: mdi:thermometer - attributes: - friendly_name: "Chamber Actual" - - name: 3d_printer_hotend_target - unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f740" - state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["target"] | float(0) | round(1) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "°C" - icon: mdi:thermometer - attributes: - friendly_name: "Hotend Target" - - name: 3d_printer_hotend_actual - unique_id: "192.168.2.317c2593fb-a7dd-4ed7-a865-2e4dddddbb40" - state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["temperature"] | float(0) | round(1) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "°C" - icon: mdi:thermometer - attributes: - friendly_name: "Hotend Actual" - - name: 3d_printer_hotend_power - unique_id: "192.168.2.31c57a1e35-92fa-403e-b3cf-ec1e97ef94cf" - state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["power"] | float(0) | round(3) * 100}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "%" - icon: mdi:flash - attributes: - friendly_name: "Hotend Power" - - name: 3d_printer_bed_target - unique_id: "192.168.2.31eadec415-c281-4814-8fba-17c6c42670ec" - state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["target"] | float(0) | round(1) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "°C" - icon: mdi:thermometer - attributes: - friendly_name: "Bed Target" - - name: 3d_printer_bed_actual - unique_id: "192.168.2.31cb33503c-c5d4-4e06-b79b-c1cd64ca2d7a" - state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["temperature"] | float(0) | round(1) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "°C" - icon: mdi:thermometer - attributes: - friendly_name: "Bed Actual" - - name: 3d_printer_bed_power - unique_id: "192.168.2.31abb2fc5b-8173-4131-a942-fb11e30f4efa" - state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["power"] | float(0) | round(3) * 100 }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "%" - icon: mdi:flash - attributes: - friendly_name: "Bed Power" - - name: 3d_printer_state - unique_id: "192.168.2.31758ce84c-3210-4f29-b6b3-38139180de96" - state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["state"] }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: > - {% set val = states.sensor.printer_3d_sensor.attributes["print_stats"]["state"] %} - {% if val == 'standby' %} - mdi:sleep - {% elif val == 'error' %} - mdi:alert-circle - {% elif val == 'printing' %} - mdi:printer-3d-nozzle - {% elif val == 'paused' %} - mdi:pause-circle - {% elif val == 'complete' %} - mdi:printer-3d - {% else %} - mdi:help-circle - {% endif %} - attributes: - friendly_name: "Printer State" - - name: 3d_printer_current_print - unique_id: "192.168.2.313a0f3144-a801-422f-adb6-e2ed35796072" - state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["filename"]}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:file - attributes: - friendly_name: "Current Print" - - name: 3d_printer_current_progress - unique_id: "192.168.2.318ec7163b-be00-4a10-8051-48cf9a260a29" - state: '{{ ((states.sensor.printer_3d_sensor.attributes["display_status"]["progress"])*100) | round(0, "floor") }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "%" - icon: mdi:progress-clock - attributes: - friendly_name: "Progress" - - name: 3d_printer_print_time - unique_id: "192.168.2.31e1ac0fac-e8ba-4e05-8fa0-c8d8076f9f63" - state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"] |timestamp_custom("%H:%M:%S", 0)}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:camera-timer - attributes: - friendly_name: "Time Elapsed" - - name: 3d_printer_time_remaining - unique_id: "192.168.2.31e12c8def-5ebe-43a4-9f75-605a0c46fc0f" - state: '{{ (((states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]/states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]- states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]) if states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]>0 else 0)) | timestamp_custom("%H:%M:%S", 0)}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:timer-sand - attributes: - friendly_name: "Time Remaining" - - name: 3d_printer_eta - unique_id: "192.168.2.31a2b57068-9a32-4d2c-8cc9-57d2389a9082" - state: '{{ (as_timestamp(now())+((states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]/states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]- states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]) if states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]>0 else 0)) | timestamp_custom("%H:%M:%S", 1)}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:av-timer - attributes: - friendly_name: "ETA" - - name: 3d_printer_message - unique_id: "192.168.2.310c418c0d-e59e-4d4e-aa11-8fae53df58f8" - state: '{{ states.sensor.printer_3d_sensor.attributes["display_status"]["message"]}}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:message-cog - attributes: - friendly_name: "Message" - - name: 3d_printer_layer_height - unique_id: "192.168.2.316cca455d-c327-4567-b886-4d7f99714265" - state: '{{ states.sensor.printer_3d_file_metadata.attributes["layer_height"] | float(0) }}' - availability: > - {% set items = ['sensor.printer_3d_file_metadata'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "mm" - icon: mdi:arrow-collapse-down - attributes: - friendly_name: "Layer Height" - - name: 3d_printer_object_height - unique_id: "192.168.2.316d6d9dc0-9a02-4ce4-a797-c84b42e011a6" - state: '{{ (states.sensor.printer_3d_file_metadata.attributes["object_height"] | float(0)) - (states.sensor.printer_3d_file_metadata.attributes["layer_height"] | float(0)) }}' - availability: > - {% set items = ['sensor.printer_3d_file_metadata'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "mm" - icon: mdi:arrow-expand-vertical - attributes: - friendly_name: "Object Height" - - name: 3d_printer_current_height - unique_id: "192.168.2.31d440e568-d4d1-4b3f-85c4-fdacd68c0e1a" - state: '{{ states.sensor.printer_3d_sensor.attributes["gcode_move"]["gcode_position"][2] | float(0) | round(2) }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - unit_of_measurement: "mm" - icon: mdi:arrow-collapse-down - attributes: - friendly_name: "Current Height" - - name: 3d_printer_current_layer - unique_id: "192.168.2.316a77acc1-8134-4354-b2f6-390adab81993" - state: '{{ (states("sensor.3d_printer_current_height")|float(0) / states("sensor.3d_printer_layer_height")|float(0))|round(0) }}' - availability: > - {% set items = ['sensor.3d_printer_current_height','sensor.3d_printer_layer_height'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:counter - attributes: - friendly_name: "Current Layer" - - name: 3d_printer_total_layers - unique_id: "192.168.2.3153cfe906-28ec-44cd-926a-b08ffb8766e5" - state: '{{ (states("sensor.3d_printer_object_height")|float(0) / states("sensor.3d_printer_layer_height")|float(0))|round(0) }}' - availability: > - {% set items = ['sensor.3d_printer_object_height','sensor.3d_printer_layer_height'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:counter - attributes: - friendly_name: "Total Layer" - - name: 3d_printer_actual_layer - unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f742" - state: '{{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["current_layer"] }} / {{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["total_layer"] }}' - availability: > - {% set items = ['sensor.printer_3d_sensor'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:layers-triple - attributes: - friendly_name: "Actual Layer" - - name: 3d_printer_object_thumbnails - unique_id: "192.168.2.3159b37837-b751-4d31-98c2-516a52edf833" - state: > - {% set dir = states('sensor.3d_printer_current_print') %} - {% set thumb = state_attr('sensor.printer_3d_file_metadata','thumbnails') %} - {% set img = (thumb | last).relative_path if thumb else 'not available' %} - {{ (dir.split('/')[:-1] + [img]) | join('/') }} - availability: > - {% set items = ['sensor.printer_3d_file_metadata'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:image - attributes: - friendly_name: "Object Thumbnails" - - name: 3d_printer_state_message - unique_id: "192.168.2.319a5184c9-ac5b-44a9-a691-2b67c243d197" - state: '{{ states.sensor.printer_3d_info.attributes["state_message"] }}' - availability: > - {% set items = ['sensor.printer_3d_info'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:message-cog - attributes: - friendly_name: "State Message" - - name: 3d_printer_heaters_power - unique_id: "192.168.2.313988e9be-c910-4634-8205-38d53170d5a5" - state: '{{ states("sensor.3d_printer_bed_power")|float(0) | round(1) }}% / {{ states("sensor.3d_printer_hotend_power")|float(0) | round(1) }}%' - availability: > - {% set items = ['sensor.3d_printer_bed_power','sensor.3d_printer_hotend_power'] %} - {{ expand(items)|rejectattr('state','in',['unknown','unavailable']) - |list|count == items|count }} - icon: mdi:meter-electric-outline - attributes: - friendly_name: "Bed and Nozzle Power" diff --git a/packages/openhasp_lounge1_wallplate.yaml b/packages/openhasp_lounge1_wallplate.yaml new file mode 100644 index 0000000..6c777a9 --- /dev/null +++ b/packages/openhasp_lounge1_wallplate.yaml @@ -0,0 +1,131 @@ +############################################################################### +# PACKAGE: OpenHASP Thermostat (MQTT-only, all BUTTON objects) +# NODE/TOPIC ROOT: hasp/openhasp_1/ +############################################################################### + +input_number: + lounge_setpoint: + name: Lounge Setpoint + min: 10 + max: 30 + step: 0.5 + mode: box + unit_of_measurement: "C" + +############################################################################### +# AUTOMATIONS +############################################################################### +automation: + # --- PUSH LABEL TEXTS (on buttons) ---------------------------------------- + - id: hasp_temp_to_btn_text + alias: "HASP: update temp (p1b1.text)" + mode: queued + trigger: + - platform: state + entity_id: sensor.lounge_inside_temperature + action: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b1.text" + payload: "{{ states('sensor.lounge_inside_temperature')|float(0)|round(1) }} C" + + - id: hasp_setpoint_to_btn_text + alias: "HASP: update setpoint (p1b2.text)" + mode: queued + trigger: + - platform: state + entity_id: input_number.lounge_setpoint + action: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.text" + payload: "Set: {{ states('input_number.lounge_setpoint')|float(0)|round(1) }} C" + + - id: hasp_heater_status_to_btn_text + alias: "HASP: update heater status (p1b5.text)" + mode: queued + trigger: + - platform: state + entity_id: switch.lounge_heater + action: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b5.text" + payload: > + {% if is_state('sensor.gas_heater_operating','ON') %} + HEAT ON + {% else %} + HEAT OFF + {% endif %} + + # --- BUTTON HANDLING FROM THE PLATE (listens on /state/) ------------------ + - id: hasp_btn_up_increment + alias: "HASP: p1b3 up -> increment setpoint" + mode: queued + trigger: + - platform: mqtt + topic: "hasp/openhasp_1/state/p1b4" + condition: + - condition: template + value_template: "{{ trigger.payload_json.event|default('') == 'up' }}" + action: + - service: input_number.increment + target: + entity_id: input_number.lounge_setpoint + + - id: hasp_btn_down_decrement + alias: "HASP: p1b4 up -> decrement setpoint" + mode: queued + trigger: + - platform: mqtt + topic: "hasp/openhasp_1/state/p1b3" + condition: + - condition: template + value_template: "{{ trigger.payload_json.event|default('') == 'up' }}" + action: + - service: input_number.decrement + target: + entity_id: input_number.lounge_setpoint + + # --- PUSH INITIAL VALUES ON HA START / PANEL ONLINE ----------------------- + - id: hasp_push_on_ha_start + alias: "HASP: push texts on HA start" + trigger: + - platform: homeassistant + event: start + action: + - service: script.hasp_push_all_texts + + - id: hasp_push_on_plate_birth + alias: "HASP: push texts on panel birth" + trigger: + - platform: mqtt + topic: "hasp/openhasp_1/state" + action: + - service: script.hasp_push_all_texts + +############################################################################### +# SCRIPT: Push all texts to buttons +############################################################################### +script: + hasp_push_all_texts: + alias: "HASP: push all texts" + mode: queued + sequence: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b1.text" + payload: "{{ states('sensor.lounge_inside_temperature')|float(0)|round(1) }} C" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.text" + payload: "Set: {{ states('input_number.lounge_setpoint')|float(0)|round(1) }} C" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b5.text" + payload: > + {% if is_state('sensor.gas_heater_operating','ON') %} + HEAT ON + {% else %} + HEAT OFF + {% endif %}