From d7884770fe0691e81c9557269498871ba55651a5 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 5 Feb 2026 21:28:48 +1300 Subject: [PATCH] yaml tidyups for esphome --- .ha_run.lock | 2 +- esphome/esp-downstbathtowelrail.yaml | 97 ++++++--- esphome/esp-entbtproxy.yaml | 2 + esphome/esp-entrancelightswitch.yaml | 1 + esphome/esp-garagebtproxy.yaml | 2 + esphome/esp-loungemainsdimmer1.yaml | 9 +- esphome/esp-loungemiddleswitch.yaml | 196 +++++++++++------- esphome/esp-mainbathfancombo.yaml | 121 ++++++++--- esphome/esp-mainbathlights.yaml | 172 --------------- ...ghts2.yaml => esp-mainbathroomlights.yaml} | 68 +++--- esphome/esp-masterbathtowelrail.yaml | 103 ++++++--- esphome/esp-poollightpower2.yaml | 158 +++++++------- esphome/esp-poolpumppower.yaml | 186 ++++++++++------- packages/robotdyn_dimmers.yaml | 13 +- packages/shower_controls.yaml | 42 +++- 15 files changed, 633 insertions(+), 539 deletions(-) delete mode 100644 esphome/esp-mainbathlights.yaml rename esphome/{esp-garageentrylights2.yaml => esp-mainbathroomlights.yaml} (64%) diff --git a/.ha_run.lock b/.ha_run.lock index 9d204c5..c872fe4 100644 --- a/.ha_run.lock +++ b/.ha_run.lock @@ -1 +1 @@ -{"pid": 67, "version": 1, "ha_version": "2026.1.3", "start_ts": 1769216420.8243344} \ No newline at end of file +{"pid": 67, "version": 1, "ha_version": "2026.2.0", "start_ts": 1770279860.4199212} \ No newline at end of file diff --git a/esphome/esp-downstbathtowelrail.yaml b/esphome/esp-downstbathtowelrail.yaml index f83fa14..7ab7388 100644 --- a/esphome/esp-downstbathtowelrail.yaml +++ b/esphome/esp-downstbathtowelrail.yaml @@ -3,15 +3,16 @@ # DOWNSTAIRS BATHROOM HEATED TOWEL RAIL # Controlled by a Sonoff Basic # -# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable +# V2.2 2026-01-30 Added Morning/Evening Timer enable switches (gate timer windows) # V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # V1.1 2025-04-12 Fixes to timers and offline modes # V1.0 2025-02-14 Initial Version -# +# # INSTRUCTIONS # - It allows the device to work in a standalone timer style operation # - The timer has a morning and evening time (but no weekday/weekend settings) +# - Morning Timer Enable / Evening Timer Enable switches gate their timer windows (default ON and restored on reboot) # - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) @@ -23,7 +24,7 @@ # - Home Assistant entities are set so that BOOST mode can be pressed with a button and other modes selectable with a dropdown # - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 30 seconds (with ~2 secs in between to allow it to boot) # -# MQTT Commands +# MQTT Commands # Values will be set in place on the update_interval time, not immediately # Use 00:00 in 24hr format for time setting. (Note there is no weekday/weekend setting) # mqtt_timer_topic/morning-on/06:00 : Time device will go on @@ -54,10 +55,10 @@ substitutions: device_name: "esp-downstbathtowelrail" friendly_name: "Downstairs Bathroom Towelrail" description_comment: "Heated Towel Rail, Downstairs Bathroom :: Sonoff Basic" - device_area: "Downstairs Flat" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + device_area: "Downstairs Flat" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "Sonoff Technologies.Sonoff Basic V1" # Project Details + project_name: "Sonoff Technologies.Sonoff Basic V1" # Project Details project_version: "v2.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords @@ -105,10 +106,10 @@ packages: 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 @@ -191,10 +192,10 @@ preferences: mdns: disabled: true -########################################################################################## +########################################################################################## # 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) @@ -212,7 +213,34 @@ switch: pin: GPIO12 id: relay restore_mode: RESTORE_DEFAULT_OFF - icon: "${relay_icon}" + icon: "${relay_icon}" + + #################################################### + # Timer Enable Switches + # These gate the schedule windows in TIMER mode only. + # Default and restored state is ON. + #################################################### + - platform: template + name: "Morning Timer Enable" + id: morning_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + + - platform: template + name: "Evening Timer Enable" + id: evening_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state ########################################################################################## # Global Variables for use in automations etc @@ -370,7 +398,7 @@ text_sensor: } ############################ - # Boost duration => integer minutes (1–1439) + # Boost duration => integer minutes (1-1439) ############################ - platform: mqtt_subscribe name: "Boost Duration" @@ -401,12 +429,12 @@ text_sensor: - lambda: |- // Check only the first character for mode char c = x.c_str()[0]; - if (c == 'T') { // “TIMER” + if (c == 'T') { // "TIMER" id(operation_mode) = 2; - } else if (c == 'O') { // “ON” or “OFF” - // second letter N→ON, F→OFF + } else if (c == 'O') { // "ON" or "OFF" + // second letter N->ON, F->OFF id(operation_mode) = (x.size() > 1 && x[1] == 'N') ? 1 : 0; - } else if (c == 'B') { // “BOOST” + } else if (c == 'B') { // "BOOST" id(operation_mode) = 3; id(boost_timer) = 0; } else { @@ -514,7 +542,7 @@ binary_sensor: id(relay).turn_on(); id(operation_mode) = 3; } - + - platform: template name: "Relay Status" lambda: |- @@ -533,7 +561,7 @@ sensor: update_interval: "${update_interval}" lambda: |- return id(boost_duration); - + - platform: template name: "Mins from Midnight" id: mins_from_midnight @@ -570,7 +598,6 @@ sensor: // never return negative return rem > 0 ? rem : 0; - ################################################################################################# # BUTTON COMPONENT # https://esphome.io/components/button/index.html @@ -587,6 +614,7 @@ button: id(operation_mode) = 3; # 2) immediately re-evaluate relay state - script.execute: evaluate_relay_state + - platform: template name: "Default timer settings" id: default_timer_settings_button @@ -603,6 +631,9 @@ button: id(operation_mode)= 2; id(boost_timer) = 0; ESP_LOGI("timer","Default timer settings applied"); + # Restore timer enable switches to ON (defaults) + - switch.turn_on: morning_timer_enable + - switch.turn_on: evening_timer_enable - script.execute: evaluate_relay_state ################################################################################################# @@ -658,25 +689,36 @@ script: return; } - // OFF → always off + // OFF -> always off if (mode == 0) { id(relay).turn_off(); return; } - // ON → always on + // ON -> always on if (mode == 1) { id(relay).turn_on(); return; } - // TIMER → follow schedule windows + // TIMER -> follow schedule windows (gated by enable switches) { bool should_on = false; - if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) - should_on = true; - if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) - should_on = true; + + // Morning window only applies when Morning Timer Enable is enabled + if (id(morning_timer_enable).state) { + if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) { + should_on = true; + } + } + + // Evening window only applies when Evening Timer Enable is enabled + if (id(evening_timer_enable).state) { + if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) { + should_on = true; + } + } + if (should_on) id(relay).turn_on(); else id(relay).turn_off(); } @@ -690,7 +732,7 @@ interval: - interval: "1min" then: - lambda: |- - // — update current_mins via SNTP or fallback + // - update current_mins via SNTP or fallback if (!id(sntp_time).now().is_valid()) { id(current_mins)++; if (id(current_mins) >= 1440) id(current_mins) = 0; @@ -699,7 +741,7 @@ interval: id(current_mins) = now.hour * 60 + now.minute; } - // — if in BOOST, advance boost_timer and expire when done + // - if in BOOST, advance boost_timer and expire when done if (id(operation_mode) == 3) { id(boost_timer)++; if (id(boost_timer) >= id(boost_duration)) { @@ -708,4 +750,3 @@ interval: } } - script.execute: evaluate_relay_state - diff --git a/esphome/esp-entbtproxy.yaml b/esphome/esp-entbtproxy.yaml index 180a333..14d3c56 100644 --- a/esphome/esp-entbtproxy.yaml +++ b/esphome/esp-entbtproxy.yaml @@ -83,6 +83,8 @@ esp32: #type: arduino type: esp-idf #Suggested Use ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. version: recommended #recommended, latest or dev + advanced: + minimum_chip_revision: "3.1" # This makes the binary slightly smaller ############################################# # ESPHome Logging Enable diff --git a/esphome/esp-entrancelightswitch.yaml b/esphome/esp-entrancelightswitch.yaml index 1f20d3b..c4f05c6 100644 --- a/esphome/esp-entrancelightswitch.yaml +++ b/esphome/esp-entrancelightswitch.yaml @@ -142,6 +142,7 @@ binary_sensor: number: GPIO16 mode: INPUT inverted: True + use_interrupt: false # GPIO16 pin doesn't support interrupts so use polling. Only supresses a warning. name: "Button 1: ${switch_1_name}" on_press: - switch.toggle: Relay_1 diff --git a/esphome/esp-garagebtproxy.yaml b/esphome/esp-garagebtproxy.yaml index f028a2f..f16efed 100644 --- a/esphome/esp-garagebtproxy.yaml +++ b/esphome/esp-garagebtproxy.yaml @@ -98,6 +98,8 @@ esp32: #type: arduino type: esp-idf #Suggested Use ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. version: recommended #recommended, latest or dev + advanced: + minimum_chip_revision: "3.1" # This makes the binary slightly smaller ############################################# # ESPHome Logging Enable diff --git a/esphome/esp-loungemainsdimmer1.yaml b/esphome/esp-loungemainsdimmer1.yaml index ae92186..84364d4 100644 --- a/esphome/esp-loungemainsdimmer1.yaml +++ b/esphome/esp-loungemainsdimmer1.yaml @@ -119,11 +119,10 @@ output: gate_pin: GPIO12 zero_cross_pin: number: GPIO14 - mode: - input: true - inverted: yes - method: LEADING - #init_with_half_cycle: true + mode: INPUT_PULLUP + inverted: true + method: leading + init_with_half_cycle: false light: - platform: monochromatic diff --git a/esphome/esp-loungemiddleswitch.yaml b/esphome/esp-loungemiddleswitch.yaml index c704601..d150cdb 100644 --- a/esphome/esp-loungemiddleswitch.yaml +++ b/esphome/esp-loungemiddleswitch.yaml @@ -17,14 +17,14 @@ # If NOT using a secrets file, just replace these with the passwords etc (in quotes) ########################################################################################## substitutions: - # Device Naming + # Device Naming device_name: "esp-loungemiddleswitch" friendly_name: "Lounge Middle Lightswitch (3)" description_comment: "Lounge Main Lightswitch using a Zemismart KS-811 Triple Push Button. Downlights South (1), Downlights Middle (2), Downlights North (3)" - device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "Zemismart Technologies.KS-811 Triple" # Project Details + project_name: "Zemismart Technologies.KS-811 Triple" # Project Details project_version: "v3.5" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords & Secrets @@ -76,7 +76,7 @@ packages: 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) #### @@ -101,22 +101,35 @@ esphome: name: "${project_name}" version: "${project_version}" +######################################################################################### +# Home Assistant API +######################################################################################### api: on_client_connected: then: - logger.log: level: INFO - format: "API connected: applying override (override=%s)" - args: ['id(ha_loss_override).state ? "true" : "false"'] + format: "API connected: override=%s, ha_loss_test=%s" + args: + - 'id(ha_loss_override).state ? "true" : "false"' + - 'id(ha_loss_test).state ? "true" : "false"' - if: condition: - switch.is_on: ha_loss_override + switch.is_on: ha_loss_test then: - - switch.turn_on: Relay_1 - - switch.turn_on: Relay_2 + - logger.log: + level: WARN + format: "HA Loss Test is ON: not applying relay override on connect" else: - - switch.turn_off: Relay_1 - - switch.turn_off: Relay_2 + - if: + condition: + switch.is_on: ha_loss_override + then: + - switch.turn_on: Relay_1 + - switch.turn_on: Relay_2 + else: + - switch.turn_off: Relay_1 + - switch.turn_off: Relay_2 on_client_disconnected: then: @@ -133,13 +146,13 @@ esp8266: early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True. board_flash_mode: dout # Default is dout -######################################################################################### +######################################################################################### # 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) + 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 @@ -164,15 +177,13 @@ binary_sensor: number: GPIO16 mode: INPUT inverted: True - use_interrupt: false # This pin doesn't support interrupts so use polling. Only supresses a warning. + use_interrupt: false # This pin doesn't support interrupts so use polling. Only suppresses a warning. name: "Button 1: ${switch_1_name}" on_multi_click: - # --- Single short click --- - # Online (and override ON): use HA's last scene to decide. - # If last != "All Off" → send "All Off" - # If last == "All Off" → send "Bright" - # Offline (and override ON): toggle physical Relay_1 + # Online (and override ON, and NOT test mode): use HA's last scene to decide. + # Offline (override ON): toggle physical Relay_1 + # Test mode (override ON): behave like offline even if HA is connected - timing: - ON for at most 0.5s - OFF for at least 0.5s @@ -182,6 +193,8 @@ binary_sensor: and: - switch.is_on: ha_loss_override - api.connected: + - not: + switch.is_on: ha_loss_test then: - if: condition: @@ -197,7 +210,7 @@ binary_sensor: scene_name: "All Off" - logger.log: level: INFO - format: "BTN1: Single click (online) → Last scene was not 'All Off' → Set 'All Off'" + format: "BTN1: Single click (online) -> Last scene was not 'All Off' -> Set 'All Off'" else: - homeassistant.service: service: script.lounge_set_scene @@ -205,15 +218,16 @@ binary_sensor: scene_name: "Bright" - logger.log: level: INFO - format: "BTN1: Single click (online) → Last scene was 'All Off' → Set 'Bright'" + format: "BTN1: Single click (online) -> Last scene was 'All Off' -> Set 'Bright'" else: - # OFFLINE path: only act if override ON; toggle physical relay - if: condition: and: - switch.is_on: ha_loss_override - - not: - api.connected: + - or: + - not: + api.connected: + - switch.is_on: ha_loss_test then: - if: condition: @@ -222,18 +236,18 @@ binary_sensor: - switch.turn_off: Relay_1 - logger.log: level: INFO - format: "BTN1: Single click (offline) → Relay_1 OFF" + format: "BTN1: Single click (offline/test) -> Relay_1 OFF" else: - switch.turn_on: Relay_1 - logger.log: level: INFO - format: "BTN1: Single click (offline) → Relay_1 ON" + format: "BTN1: Single click (offline/test) -> Relay_1 ON" else: - logger.log: level: INFO format: "BTN1: Single click ignored (override OFF)" -# GPIO05 for second button (only for KS-811-2 Double or -3 Triple) + # GPIO05 for second button (only for KS-811-2 Double or -3 Triple) - platform: gpio pin: number: GPIO05 @@ -242,10 +256,8 @@ binary_sensor: name: "Button 2: ${switch_2_name}" on_multi_click: # --- Single short click --- - # Online (and override ON): use HA's last scene to decide. - # If last != "All Off" → send "All Off" - # If last == "All Off" → send "Bright" - # Offline (and override ON): toggle physical Relay_1 + # Online (override ON, NOT test mode): set scene + # Offline/test (override ON): toggle Relay_2 - timing: - ON for at most 0.5s - OFF for at least 0.5s @@ -255,22 +267,25 @@ binary_sensor: and: - switch.is_on: ha_loss_override - api.connected: + - not: + switch.is_on: ha_loss_test then: - - homeassistant.service: - service: script.lounge_set_scene - data: - scene_name: "TV General" - - logger.log: - level: INFO - format: "BTN2: Single click (online) → Set 'TV General' Scene" + - homeassistant.service: + service: script.lounge_set_scene + data: + scene_name: "Movie" + - logger.log: + level: INFO + format: "BTN2: Single click (online) -> Set 'Movie' Scene" else: - # OFFLINE path: only act if override ON; toggle physical relay - if: condition: and: - switch.is_on: ha_loss_override - - not: - api.connected: + - or: + - not: + api.connected: + - switch.is_on: ha_loss_test then: - if: condition: @@ -279,18 +294,18 @@ binary_sensor: - switch.turn_off: Relay_2 - logger.log: level: INFO - format: "BTN2: Single click (offline) → Relay_2 OFF" + format: "BTN2: Single click (offline/test) -> Relay_2 OFF" else: - - switch.turn_on: Relay_1 + - switch.turn_on: Relay_2 - logger.log: level: INFO - format: "BTN2: Single click (offline) → Relay_2 ON" + format: "BTN2: Single click (offline/test) -> Relay_2 ON" else: - logger.log: level: INFO - format: "BTN: Single click ignored" + format: "BTN2: Single click ignored (override OFF)" -# GPIO04 for third button (only for KS-811-3 Triple) + # GPIO04 for third button (only for KS-811-3 Triple) - platform: gpio pin: number: GPIO4 @@ -299,10 +314,8 @@ binary_sensor: name: "Button 3: ${switch_3_name}" on_multi_click: # --- Single short click --- - # Online (and override ON): use HA's last scene to decide. - # If last != "All Off" → send "All Off" - # If last == "All Off" → send "Bright" - # Offline (and override ON): toggle physical Relay_1 + # Online (override ON, NOT test mode): set scene + # Offline/test (override ON): toggle Relay_3 - timing: - ON for at most 0.5s - OFF for at least 0.5s @@ -312,50 +325,51 @@ binary_sensor: and: - switch.is_on: ha_loss_override - api.connected: + - not: + switch.is_on: ha_loss_test then: - - homeassistant.service: - service: script.lounge_set_scene - data: - scene_name: "Reading On/Off" - - logger.log: - level: INFO - format: "BTN2: Single click (online) → Set 'Reaading On/Off' Scene" + - homeassistant.service: + service: script.lounge_set_scene + data: + scene_name: "Reading On/Off" + - logger.log: + level: INFO + format: "BTN3: Single click (online) -> Set 'Reading On/Off' Scene" else: - # OFFLINE path: only act if override ON; toggle physical relay - if: condition: and: - switch.is_on: ha_loss_override - - not: - api.connected: + - or: + - not: + api.connected: + - switch.is_on: ha_loss_test then: - if: condition: - lambda: 'return id(Relay_2).state;' + lambda: 'return id(Relay_3).state;' then: - - switch.turn_off: Relay_2 + - switch.turn_off: Relay_3 - logger.log: level: INFO - format: "BTN2: Single click (offline) → Relay_2 OFF" + format: "BTN3: Single click (offline/test) -> Relay_3 OFF" else: - - switch.turn_on: Relay_1 + - switch.turn_on: Relay_3 - logger.log: level: INFO - format: "BTN2: Single click (offline) → Relay_2 ON" + format: "BTN3: Single click (offline/test) -> Relay_3 ON" else: - logger.log: level: INFO - format: "BTN: Single click ignored" + format: "BTN3: Single click ignored (override OFF)" ######################################################################################### # TEXT SENSOR COMPONENT -# ######################################################################################### text_sensor: - platform: homeassistant id: ha_last_scene entity_id: input_text.lounge_last_scene - # logging: helpful when testing on_value: - logger.log: level: DEBUG @@ -367,7 +381,7 @@ text_sensor: # https://esphome.io/components/switch/ ######################################################################################### switch: -# GPIO13 for KS-811 first button UNLESS it is KS-811-1 then it is GIPO12 + # GPIO13 for KS-811 first button UNLESS it is KS-811-1 then it is GPIO12 - platform: gpio name: "Relay 1: ${switch_1_name}" pin: GPIO13 @@ -392,5 +406,43 @@ switch: name: "HA Loss manual override" id: ha_loss_override optimistic: true - restore_mode: RESTORE_DEFAULT_ON + restore_mode: ALWAYS_ON + + # HA Loss Test (default OFF): when ON, force "offline behavior" even if HA is connected + - platform: template + name: "HA Loss Test" + id: ha_loss_test + optimistic: true + restore_mode: ALWAYS_OFF + on_turn_on: + - logger.log: + level: WARN + format: "HA Loss Test turned ON: forcing offline/test behavior for buttons" + on_turn_off: + - logger.log: + level: INFO + format: "HA Loss Test turned OFF: forcing Relay_1 and Relay_2 ON, then restoring last scene in 30s" + - switch.turn_on: Relay_1 + - switch.turn_on: Relay_2 + - delay: 30s + - if: + condition: + and: + - api.connected: + - lambda: |- + auto s = id(ha_last_scene).state; + return !(s.empty() || s == "unknown" || s == "unavailable"); + then: + - homeassistant.service: + service: script.lounge_set_scene + data: + scene_name: !lambda 'return id(ha_last_scene).state;' + - logger.log: + level: INFO + format: "HA Loss Test OFF: restored last scene: %s" + args: ['id(ha_last_scene).state.c_str()'] + else: + - logger.log: + level: WARN + format: "HA Loss Test OFF: cannot restore scene (API not connected or last scene unknown)" diff --git a/esphome/esp-mainbathfancombo.yaml b/esphome/esp-mainbathfancombo.yaml index 6744d51..d98ce4a 100644 --- a/esphome/esp-mainbathfancombo.yaml +++ b/esphome/esp-mainbathfancombo.yaml @@ -1,26 +1,26 @@ -############################################# -############################################# +########################################################################################## +########################################################################################## # MAIN BATHROOM FAN/HEAT COMBO SWITCH # V1.1 2025-08-26 Minor Changes (MQTT) # V1.0 2025-06-01 Initial Version -############################################# +########################################################################################## # Zemismart KS-811 Triple push button # pinout/schematic https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ # # NOTES # - # -############################################# -############################################# +########################################################################################## +########################################################################################## -############################################# +########################################################################################## # 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-mainbathfancombo" - friendly_name: "Main Bathroom Fan/Heat Combo Switch (2)" + friendly_name: "Main Bathroom Fan-Heat Combo Switch (2)" description_comment: "Main Bathroom Fan/Heat Switch using a Zemismart KS-811 Double Push Button. Extract Fan (1), IR heater (2)" device_area: "Laundry" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. @@ -84,10 +84,10 @@ packages: #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} @@ -102,47 +102,67 @@ esphome: - "-Wl,--gc-sections" # drop unused code/data #- "-fno-exceptions" # strip C++ exceptions #- "-fno-rtti" # strip C++ RTTI -# on_boot: -# priority: 200 -# then: -# - switch.turn_on: Relay_3 + on_boot: + priority: -100 + then: + - if: + condition: + switch.is_on: Relay_2 + then: + - if: + condition: + switch.is_off: Relay_1 + then: + - lambda: |- + id(relay2_forced_relay1) = true; + - switch.turn_on: Relay_1 -############################################# +########################################################################################## # ESP Platform and Framework # https://esphome.io/components/esp32.html -############################################# +########################################################################################## esp8266: board: esp01_1m early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True. board_flash_mode: dout # Default is dout -############################################# +########################################################################################## # 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 -############################################# +########################################################################################## +# GLOBAL VARIABLES +# https://esphome.io/guides/automations.html?highlight=globals#global-variables +########################################################################################## +globals: + - id: relay2_forced_relay1 + type: bool + restore_value: yes + initial_value: "false" + +########################################################################################## # STATUS LED # https://esphome.io/components/status_led.html -############################################# +########################################################################################## # Status LED for KS-811 is gpio2 -############################################# +########################################################################################## status_led: pin: number: GPIO2 inverted: yes -############################################# +########################################################################################## # BINARY SENSORS # https://esphome.io/components/binary_sensor/ -############################################# +########################################################################################## # Buttons for KS-811-2 are GPIO16, GPIO05 -############################################# +########################################################################################## binary_sensor: - platform: gpio pin: @@ -183,24 +203,63 @@ binary_sensor: topic: "${mqtt_remote_device1_command_topic}" payload: "${mqtt_remote_device1_command1}" -############################################# +########################################################################################## # SWITCH COMPONENT # https://esphome.io/components/switch/ -############################################# +########################################################################################## # Relays for KS-811-2 are GPIO13, GPIO12 -############################################# +########################################################################################## switch: - platform: gpio name: "Relay 1: Extract Fan" pin: GPIO13 id: Relay_1 - + on_turn_off: + then: + # Rule 3: If Relay_1 is turned OFF while Relay_2 is ON, turn OFF Relay_2 too + - if: + condition: + switch.is_on: Relay_2 + then: + - switch.turn_off: Relay_2 + - platform: gpio name: "Relay 2: IR Heater" pin: GPIO12 id: Relay_2 - # Always run the fan when IR heater on. Not critical for this heat/lamp product - # as it has its own failsafes and fan switching on temp, but an added safety measure on_turn_on: - switch.turn_on: "Relay_1" + then: + # Rule 2: Relay_2 implies Relay_1 must be ON + # Also capture whether Relay_2 had to force Relay_1 ON (Rule 4/5 memory) + - lambda: |- + id(relay2_forced_relay1) = !id(Relay_1).state; + - switch.turn_on: Relay_1 + on_turn_off: + then: + # Rule 4/5: + # - If Relay_1 was ON before Relay_2, keep Relay_1 ON + # - If Relay_2 was ON before Relay_1 (forced), then turn Relay_1 OFF now + - if: + condition: + lambda: |- + return id(relay2_forced_relay1); + then: + - switch.turn_off: Relay_1 + +########################################################################################## +# INTERVAL COMPONENT +# https://esphome.io/components/interval/ +########################################################################################## +# An assurance that if Relay_2 (Heater) is ever on, check that Relay_1 (Fan) is on also +########################################################################################## +interval: + - interval: 1s + then: + - if: + condition: + and: + - switch.is_on: Relay_2 + - switch.is_off: Relay_1 + then: + - switch.turn_off: Relay_2 diff --git a/esphome/esp-mainbathlights.yaml b/esphome/esp-mainbathlights.yaml deleted file mode 100644 index 6e3bf4c..0000000 --- a/esphome/esp-mainbathlights.yaml +++ /dev/null @@ -1,172 +0,0 @@ -############################################# -############################################# -# MAIN BATHROOM LIGHTSWITCH -# V1.0 2025-06-01 Initial Version -############################################# -# Zemismart KS-811 Triple push button -# pinout/schematic https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ -# -# NOTES -# - -# -############################################# -############################################# - -############################################# -# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS -# If NOT using a secrets file, just replace these with the passwords etc (in quotes) -############################################# -substitutions: - device_name: "esp-mainbathlights" - project_name: "Zemismart Technologies.KS-811-3 (Triple)" # Project Details - project_version: "v1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version - entity_prefix: "Laundry" # Simple device name where we want to prefix a sensor or switch, eg "Load" Current. - friendly_name: "Main Bathroom Lightswitch (3)" - description_comment: "Main Bathroom Lightswitch using a Zemismart KS-811 Triple Push Button. Main Lights (1), Shower Lights (2), Cabinet Lights (3)" - device_area: "Laundry" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. - 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-mainbathlights_ip - 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 - -########################################################################################## -# PACKAGES: Included Common Packages -# https://esphome.io/components/packages.html -########################################################################################## -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 - area: ${device_area} - project: - name: "${project_name}" - version: "${project_version}" -# on_boot: -# priority: 200 -# then: -# - switch.turn_on: Relay_3 - -############################################# -# ESP Platform and Framework -# https://esphome.io/components/esp32.html -############################################# -esp8266: - board: esp01_1m - early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True. - board_flash_mode: dout # Default is dout - -############################################# -# 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 - -############################################# -# STATUS LED -# https://esphome.io/components/status_led.html -############################################# -status_led: - pin: - number: GPIO2 - inverted: yes - -############################################# -# BINARY SENSORS -# https://esphome.io/components/binary_sensor/ -############################################# -binary_sensor: - - platform: gpio - pin: - number: GPIO16 - mode: INPUT - inverted: True - name: "Button 1: Main Lights" - on_press: - - switch.toggle: Relay_1 - - - platform: gpio - pin: - number: GPIO05 - mode: INPUT - inverted: True - name: "Button 2: Shower Lights" - on_press: - - switch.toggle: Relay_2 - - - platform: gpio - pin: - number: GPIO4 - mode: INPUT - inverted: True - name: "Button 3: Cabinet Lights" - on_press: - - switch.turn_off: Relay_1 - - switch.turn_off: Relay_2 - - switch.turn_off: Relay_3 - -############################################# -# SWITCH COMPONENT -# https://esphome.io/components/switch/ -############################################# -switch: - - platform: gpio - name: "Relay 1: Main Lights" - pin: GPIO13 - id: Relay_1 - on_turn_on: - - switch.turn_on: Relay_2 - - switch.turn_on: Relay_3 - - - platform: gpio - name: "Relay 2: Shower Lights" - pin: GPIO12 - id: Relay_2 - - - platform: gpio - name: "Relay 3: Cabinet Lights" - pin: GPIO14 - id: Relay_3 - - diff --git a/esphome/esp-garageentrylights2.yaml b/esphome/esp-mainbathroomlights.yaml similarity index 64% rename from esphome/esp-garageentrylights2.yaml rename to esphome/esp-mainbathroomlights.yaml index 88a8146..f61f978 100644 --- a/esphome/esp-garageentrylights2.yaml +++ b/esphome/esp-mainbathroomlights.yaml @@ -1,27 +1,28 @@ -############################################# -############################################# -# GARAGE ENTRY LIGHTSWITCH 2 (OBK Converted, BK7231N) +########################################################################################## +########################################################################################## +# MAIN BATHROOM LIGHTSWITCH 2 (OBK Converted, BK7231N) +# V2.0 2026-02-04 Reflash into new switch (old switch electrical failure, now a BK) # V1.0 2025-11-23 Initial -############################################# +########################################################################################## # Zemismart KS-811 Triple push button # pinout/schematic https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ # # NOTES # - # -############################################# -############################################# +########################################################################################## +########################################################################################## -############################################# +########################################################################################## # 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-garageentrylights2" - friendly_name: "Garage Entry Lightswitch (3) 2" - description_comment: "BK7231N - 2 Garage Entry Lightswitch using a Zemismart KS-811 Triple Push Button. Corridor Light (1), Patio Lights (2), Garage Lights (3)" - device_area: "Garage" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + device_name: "esp-mainbathroomlights" + friendly_name: "Main Bathroom Lightswitch (3) 2" + description_comment: "BK7231N - Main Bathroom Lightswitch using a Zemismart KS-811 Triple Push Button. Main Lights (1), Shower Lights (2), Cabinet Lights (3)" + device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Zemismart Technologies.KS-811 Triple BK7231N" # Project Details @@ -41,9 +42,9 @@ substitutions: relay_restore_mode: "RESTORE_DEFAULT_OFF" # Switch Naming - switch_1_name: "Corridor Lights" # Only one light (Garage Side) actually connected to this relay - switch_2_name: "Patio Lights" # Output for pario string hanging lights - switch_3_name: "Garage Lights" # This is virtual only, no power connected to 3rd relay + switch_1_name: "Main Lights" # Only one light (Garage Side) actually connected to this relay + switch_2_name: "Shower Lights" # Output for pario string hanging lights + switch_3_name: "Cabinet Lights" # This is virtual only, no power connected to 3rd relay ########################################################################################## # PACKAGES: Included Common Packages @@ -91,10 +92,10 @@ wifi: mqtt: keepalive: 15s # overriding the value from the common package -############################################# +########################################################################################## # ESPHome # https://esphome.io/components/esphome.html -############################################# +########################################################################################## esphome: name: "${device_name}" friendly_name: "${friendly_name}" @@ -108,45 +109,45 @@ esphome: # then: # - switch.turn_on: Relay_3 -############################################# +########################################################################################## # BUILD FOR BK CHIPSET # bk72xx # https://devices.esphome.io/board/bk72xx/ -############################################# +########################################################################################## bk72xx: board: generic-bk7231n-qfn32-tuya -############################################# +########################################################################################## # ESP Platform and Framework # https://esphome.io/components/esp32.html -############################################# +########################################################################################## #esp8266: # board: esp01_1m # early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True. # board_flash_mode: dout # Default is dout -############################################# +########################################################################################## # 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) id: logger_id -############################################# +########################################################################################## # STATUS LED # https://esphome.io/components/status_led.html -############################################# +########################################################################################## status_led: pin: number: P22 #GPIO22 inverted: true -############################################# +########################################################################################## # BINARY SENSORS # https://esphome.io/components/binary_sensor/ -############################################# +########################################################################################## binary_sensor: - platform: gpio pin: @@ -182,23 +183,30 @@ binary_sensor: name: "Button 3: ${switch_3_name}" on_press: - delay: 50ms - - switch.toggle: Relay_3 + - switch.turn_off: Relay_1 + - switch.turn_off: Relay_2 + - switch.turn_off: Relay_3 -############################################# +########################################################################################## # SWITCH COMPONENT # https://esphome.io/components/switch/ -############################################# +########################################################################################## switch: - platform: gpio name: "Relay 1: ${switch_1_name}" pin: P14 #GPIO14 id: Relay_1 restore_mode: ${relay_restore_mode} + on_turn_on: + - switch.turn_on: Relay_2 + - switch.turn_on: Relay_3 + - platform: gpio name: "Relay 2: ${switch_2_name}" pin: P16 #GPIO16 id: Relay_2 restore_mode: ${relay_restore_mode} + - platform: gpio name: "Relay 3: ${switch_3_name}" pin: P15 #GPIO15 diff --git a/esphome/esp-masterbathtowelrail.yaml b/esphome/esp-masterbathtowelrail.yaml index 661d0b1..d0b2697 100644 --- a/esphome/esp-masterbathtowelrail.yaml +++ b/esphome/esp-masterbathtowelrail.yaml @@ -3,15 +3,16 @@ # MASTER BATHROOM HEATED TOWEL RAIL # Controlled by a Sonoff Basic # -# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable +# V2.2 2026-01-30 Added Morning/Evening Timer enable switches (gate timer windows) # V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # V1.1 2025-04-12 Fixes to timers and offline modes # V1.0 2025-02-14 Initial Version -# +# # INSTRUCTIONS # - It allows the device to work in a standalone timer style operation # - The timer has a morning and evening time (but no weekday/weekend settings) +# - Morning Timer Enable / Evening Timer Enable switches gate their timer windows (default ON and restored on reboot) # - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) @@ -23,7 +24,7 @@ # - Home Assistant entities are set so that BOOST mode can be pressed with a button and other modes selectable with a dropdown # - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 30 seconds (with ~2 secs in between to allow it to boot) # -# MQTT Commands +# MQTT Commands # Values will be set in place on the update_interval time, not immediately # Use 00:00 in 24hr format for time setting. (Note there is no weekday/weekend setting) # mqtt_timer_topic/morning-on/06:00 : Time device will go on @@ -54,12 +55,12 @@ substitutions: device_name: "esp-masterbathtowelrail" friendly_name: "Master Bathroom Towelrail" description_comment: "Sonoff Basic controlling ON/OFF/Timer for the Heated Towel Rail in the Master Bathroom" - device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "Sonoff Technologies.Sonoff Basic V1" # Project Details - project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version - + project_name: "Sonoff Technologies.Sonoff Basic V1" # Project Details + project_version: "v2.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + # Passwords api_key: !secret esp-masterbathtowelrail_api_key # unfortunately you can't use substitutions inside secrets names ota_pass: !secret esp-masterbathtowelrail_ota_pass # unfortunately you can't use substitutions inside secrets names @@ -103,7 +104,7 @@ packages: 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) #### @@ -188,10 +189,10 @@ preferences: mdns: disabled: true -########################################################################################## +########################################################################################## # 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) @@ -209,7 +210,34 @@ switch: pin: GPIO12 id: relay restore_mode: RESTORE_DEFAULT_OFF - icon: "${relay_icon}" + icon: "${relay_icon}" + + #################################################### + # Timer Enable Switches + # These gate the schedule windows in TIMER mode only. + # Default and restored state is ON. + #################################################### + - platform: template + name: "Morning Timer Enable" + id: morning_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + + - platform: template + name: "Evening Timer Enable" + id: evening_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state ########################################################################################## # Global Variables for use in automations etc @@ -367,7 +395,7 @@ text_sensor: } ############################ - # Boost duration => integer minutes (1–1439) + # Boost duration => integer minutes (1-1439) ############################ - platform: mqtt_subscribe name: "Boost Duration" @@ -398,12 +426,12 @@ text_sensor: - lambda: |- // Check only the first character for mode char c = x.c_str()[0]; - if (c == 'T') { // “TIMER” + if (c == 'T') { // "TIMER" id(operation_mode) = 2; - } else if (c == 'O') { // “ON” or “OFF” - // second letter N→ON, F→OFF + } else if (c == 'O') { // "ON" or "OFF" + // second letter N->ON, F->OFF id(operation_mode) = (x.size() > 1 && x[1] == 'N') ? 1 : 0; - } else if (c == 'B') { // “BOOST” + } else if (c == 'B') { // "BOOST" id(operation_mode) = 3; id(boost_timer) = 0; } else { @@ -495,13 +523,13 @@ number: - platform: template name: "Operation Mode Number" id: operation_mode_number - internal: true # ← hides it from HA + internal: true # <- hides it from HA min_value: 0 max_value: 3 step: 1 set_action: - lambda: |- - // x holds the incoming value (0–3) + // x holds the incoming value (0-3) id(operation_mode) = int(x); ########################################################################################## @@ -532,7 +560,7 @@ binary_sensor: id(relay).turn_on(); id(operation_mode) = 3; } - + - platform: template name: "Relay Status" lambda: |- @@ -551,7 +579,7 @@ sensor: update_interval: "${update_interval}" lambda: |- return id(boost_duration); - + - platform: template name: "Mins from Midnight" id: mins_from_midnight @@ -588,7 +616,6 @@ sensor: // never return negative return rem > 0 ? rem : 0; - ################################################################################################# # BUTTON COMPONENT # https://esphome.io/components/button/index.html @@ -605,6 +632,7 @@ button: id(operation_mode) = 3; # 2) immediately re-evaluate relay state - script.execute: evaluate_relay_state + - platform: template name: "Default timer settings" id: default_timer_settings_button @@ -621,6 +649,9 @@ button: id(operation_mode)= 2; id(boost_timer) = 0; ESP_LOGI("timer","Default timer settings applied"); + # Restore timer enable switches to ON (defaults) + - switch.turn_on: morning_timer_enable + - switch.turn_on: evening_timer_enable - script.execute: evaluate_relay_state ################################################################################################# @@ -676,25 +707,36 @@ script: return; } - // OFF → always off + // OFF -> always off if (mode == 0) { id(relay).turn_off(); return; } - // ON → always on + // ON -> always on if (mode == 1) { id(relay).turn_on(); return; } - // TIMER → follow schedule windows + // TIMER -> follow schedule windows (gated by enable switches) { bool should_on = false; - if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) - should_on = true; - if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) - should_on = true; + + // Morning window only applies when Morning Timer Enable is enabled + if (id(morning_timer_enable).state) { + if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) { + should_on = true; + } + } + + // Evening window only applies when Evening Timer Enable is enabled + if (id(evening_timer_enable).state) { + if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) { + should_on = true; + } + } + if (should_on) id(relay).turn_on(); else id(relay).turn_off(); } @@ -708,7 +750,7 @@ interval: - interval: "1min" then: - lambda: |- - // — update current_mins via SNTP or fallback + // - update current_mins via SNTP or fallback if (!id(sntp_time).now().is_valid()) { id(current_mins)++; if (id(current_mins) >= 1440) id(current_mins) = 0; @@ -717,7 +759,7 @@ interval: id(current_mins) = now.hour * 60 + now.minute; } - // — if in BOOST, advance boost_timer and expire when done + // - if in BOOST, advance boost_timer and expire when done if (id(operation_mode) == 3) { id(boost_timer)++; if (id(boost_timer) >= id(boost_duration)) { @@ -726,4 +768,3 @@ interval: } } - script.execute: evaluate_relay_state - diff --git a/esphome/esp-poollightpower2.yaml b/esphome/esp-poollightpower2.yaml index 5e1241f..344ec5b 100644 --- a/esphome/esp-poollightpower2.yaml +++ b/esphome/esp-poollightpower2.yaml @@ -3,7 +3,7 @@ # POOL LIGHT POWER AND TIMER 2 # Controlled by a Athom Smart Plug V2 # -# v4.0 2025-11-04 Changed to Athom V2 +# v4.0 2026-01-31 Added Morning/Evening Timer Enable switches (gate timer windows) # V3.0 2025-09-02 Pool Light mode controller fixes; Mode select forces Operation=ON; # added Mode 0=OFF; select shows OFF when relay off; enforce 7s min OFF window # V2.4 2025-06-15 Changed back to an Athom V1 (esp8266) @@ -15,6 +15,8 @@ # INSTRUCTIONS # - It allows the device to work in a standalone timer style operation # - The timer has a morning and evening time (but no weekday/weekend settings) +# - Morning Timer Enable / Evening Timer Enable switches gate their timer windows (default ON and restored on reboot) +# - Pool Light Mode can be set via the "Pool Light Mode" dropdown; selecting a mode forces Operation Mode = ON, and selecting "0=OFF" turns the relay OFF and returns Operation Mode to TIMER # - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) @@ -49,6 +51,7 @@ ########################################################################################## + ########################################################################################## # SPECIFIC DEVICE VARIABLE SUBSTITUTIONS # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -58,10 +61,10 @@ substitutions: device_name: "esp-poollightpower2" friendly_name: "Pool Light Power" description_comment: "Pool Light Power :: Athom Smart Plug Power V2" - device_area: "Outside" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. - + device_area: "Outside" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + # Project Naming - project_name: "Athom Technology.Smart Plug V2" # Project Details + project_name: "Athom Technology.Smart Plug V2" # Project Details project_version: "v3.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords @@ -74,7 +77,7 @@ substitutions: update_interval: "10s" # update time for for general sensors etc # Device Settings - relay_icon: "mdi:power-socket-au" + relay_icon: "mdi:power-socket-au" current_limit : "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. mqtt_timer_topic: "viewroad-commands/poollight-timer" # Topics you will use to change stuff boost_duration_default: "180" # Minutes to stay ON in BOOST mode before reverting to TIMER @@ -82,7 +85,7 @@ substitutions: morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) evening_on_default: "1140" # Default in minutes from midnight. Default 19:00 => 1140 evening_off_default: "1350" # Default in minutes from midnight. Default 22:30 => 1350 => 1440 is midnight - + ############################################################################## # POOL LIGHT MODE CONTROL (new) ############################################################################## @@ -114,7 +117,7 @@ packages: ################################################## # OPTIONAL packages (comment if you don't want them) - ################################################## + ################################################## #### HOME ASSISTANT API (choose encryption or no encryption options) #### common_api: !include @@ -122,7 +125,7 @@ packages: #file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" - + #### WEB PORTAL #### #common_webportal: !include # file: common/webportal_common.yaml @@ -132,7 +135,7 @@ packages: file: common/mqtt_common.yaml vars: local_device_name: "${device_name}" - + #### SNTP (Only use if you want/need accurate timeclocks) #### common_sntp: !include file: common/sntp_common.yaml @@ -169,7 +172,7 @@ esphome: name: "${device_name}" friendly_name: "${friendly_name}" comment: "${description_comment}" #Appears on the esphome page in HA - area: "${device_area}" + area: "${device_area}" name_add_mac_suffix: False min_version: 2024.6.0 project: @@ -202,16 +205,48 @@ preferences: mdns: disabled: true # binary size saving -########################################################################################## +########################################################################################## # 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 +########################################################################################## +# SWITCH COMPONENT +# https://esphome.io/components/switch/ +########################################################################################## +switch: + #################################################### + # Timer Enable Switches + # These gate the schedule windows in TIMER mode only. + # Default and restored state is ON. + #################################################### + - platform: template + name: "Morning Timer Enable" + id: morning_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + + - platform: template + name: "Evening Timer Enable" + id: evening_timer_enable + restore_mode: RESTORE_DEFAULT_ON + icon: "mdi:timer-outline" + optimistic: true + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + ########################################################################################## # Global Variables for use in automations etc # https://esphome.io/guides/automations.html?highlight=globals#global-variables @@ -433,7 +468,7 @@ text_sensor: char *endptr; long v = strtol(x.c_str(), &endptr, 10); - // invalid if nothing parsed, trailing chars, or out of 0–1439 + // invalid if nothing parsed, trailing chars, or out of 0-1439 if (endptr == x.c_str() || *endptr != '\0' || v < 0 || v > 1439) { ESP_LOGE("boost_time", "Invalid boost_time '%s'", x.c_str()); } else { @@ -546,31 +581,6 @@ text_sensor: # BINARY SENSORS # https://esphome.io/components/binary_sensor/ ########################################################################################## -#binary_sensor: -# - platform: gpio -# pin: -# number: GPIO03 -# mode: INPUT_PULLUP -# inverted: true -# name: "Power Button" -# id: power_button -# filters: -# - delayed_on: 20ms -# on_click: -# - min_length: 20ms -# max_length: 500ms -# then: -# - lambda: |- -# if (id(relay).state) { -# // Relay is ON: turn it OFF and set mode to 2 (TIMER) -# id(relay).turn_off(); -# id(operation_mode) = 2; -# } else { -# // Relay is OFF: turn it ON and set mode to 3 (BOOST) -# id(relay).turn_on(); -# id(operation_mode) = 3; -# } - binary_sensor: - platform: template id: relay_status @@ -603,7 +613,6 @@ binary_sensor: id(last_off_ms) = millis(); if (!id(mode_changing)) id(current_mode) = 0; - ########################################################################################## # Sensors # https://esphome.io/components/text_sensor/index.html @@ -617,7 +626,7 @@ sensor: update_interval: "${update_interval}" lambda: |- return id(boost_duration); - + - platform: template name: "Mins from Midnight" id: mins_from_midnight @@ -654,37 +663,6 @@ sensor: // never return negative return rem > 0 ? rem : 0; - -################################################################################################# -# SWITCH COMPONENT -# https://esphome.io/components/switch/ -################################################################################################# -#switch: -# - platform: gpio -# name: "Power Output" -# pin: GPIO14 -# id: relay -# restore_mode: RESTORE_DEFAULT_OFF # Ensures the relay is restored (or off) at boot -# #internal: true # Hides the switch from Home Assistant -# icon: "${relay_icon}" -# # POOL LIGHT MODE CONTROL: detect quick vs long off periods and keep current_mode in sync -# on_turn_off: -# - lambda: |- -# id(last_off_ms) = millis(); -# if (!id(mode_changing)) id(current_mode) = 0; // reflect OFF in select when off normally -# on_turn_on: -# - lambda: |- -# if (id(mode_changing)) { -# return; -# } -# uint32_t delta = millis() - id(last_off_ms); -# if (delta >= (uint32_t)id(reset_window_ms)) { -# id(current_mode) = 1; -# } else { -# id(current_mode)++; -# if (id(current_mode) > id(max_modes)) id(current_mode) = 1; // keep internal index sane -# } - ################################################################################################# # BUTTON COMPONENT # https://esphome.io/components/button/index.html @@ -701,6 +679,7 @@ button: id(operation_mode) = 3; # 2) immediately re-evaluate relay state - script.execute: evaluate_relay_state + - platform: template name: "Default timer settings" id: default_timer_settings_button @@ -717,6 +696,9 @@ button: id(operation_mode)= 2; id(boost_timer) = 0; ESP_LOGI("timer","Default timer settings applied"); + # Restore timer enable switches to ON (defaults) + - switch.turn_on: morning_timer_enable + - switch.turn_on: evening_timer_enable - script.execute: evaluate_relay_state ################################################################################################# @@ -755,13 +737,6 @@ select: } - script.execute: evaluate_relay_state - ############################################################################## - # POOL LIGHT MODE SELECT (new) - # 0=OFF -> immediately turns relay OFF and sets Operation Mode = TIMER. - # Selecting a color (>=1) forces Operation Mode = ON. - # If target < current, do a long reset to mode 1, then pulse forward. - # Ignored modes are hidden here but still counted by pulses. - ############################################################################## ############################################################################## # POOL LIGHT MODE SELECT (updated display logic) # 0=OFF -> immediately turns relay OFF and sets Operation Mode = TIMER. @@ -909,10 +884,21 @@ script: target_on = true; } else if (mode == 2) { // TIMER bool should_on = false; - if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) - should_on = true; - if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) - should_on = true; + + // Morning window only applies when Morning Timer Enable is enabled + if (id(morning_timer_enable).state) { + if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) { + should_on = true; + } + } + + // Evening window only applies when Evening Timer Enable is enabled + if (id(evening_timer_enable).state) { + if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) { + should_on = true; + } + } + target_on = should_on; } @@ -963,7 +949,6 @@ script: - lambda: |- id(current_mode) = 1; - # Change to desired_mode, robust from OFF and without wrap on lower targets # Change to desired_mode, robust from OFF and without wrap on lower targets - id: change_to_desired_mode mode: restart @@ -986,7 +971,7 @@ script: - switch.turn_on: relay - delay: ${mode_prime_on} - lambda: |- - // If we turned ON after ≥ reset window, hardware is at Mode 1 + // If we turned ON after >= reset window, hardware is at Mode 1 if ((millis() - id(last_off_ms)) >= (uint32_t) id(reset_window_ms)) { id(current_mode) = 1; } @@ -1028,7 +1013,6 @@ script: - lambda: |- id(mode_changing) = false; - ################################################################################################# # INTERVAL COMPONENT # https://esphome.io/components/interval.html @@ -1038,7 +1022,7 @@ interval: - interval: "1min" then: - lambda: |- - // — update current_mins via SNTP or fallback + // - update current_mins via SNTP or fallback if (!id(sntp_time).now().is_valid()) { id(current_mins)++; if (id(current_mins) >= 1440) id(current_mins) = 0; @@ -1047,7 +1031,7 @@ interval: id(current_mins) = now.hour * 60 + now.minute; } - // — if in BOOST, advance boost_timer and expire when done + // - if in BOOST, advance boost_timer and expire when done if (id(operation_mode) == 3) { id(boost_timer)++; if (id(boost_timer) >= id(boost_duration)) { diff --git a/esphome/esp-poolpumppower.yaml b/esphome/esp-poolpumppower.yaml index ebabe16..cfa7b5c 100644 --- a/esphome/esp-poolpumppower.yaml +++ b/esphome/esp-poolpumppower.yaml @@ -6,14 +6,16 @@ # dashboard_import: # package_import_url: github://athom-tech/esp32-configs/athom-smart-plug.yaml # +# V2.4 2026-01-31 Added Morning/Evening Timer Enable switches (gate timer windows) # V2.3 2025-06-19 Minor changes to remember mode in reboot and to ensure offline functionality # V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable # V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups -# +# # INSTRUCTIONS # - It allows the device to work in a standalone timer style operation # - The timer has a morning and evening time (but no weekday/weekend settings) +# - Morning Timer Enable / Evening Timer Enable switches gate their timer windows (default ON and restored on reboot) # - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) @@ -25,7 +27,7 @@ # - Home Assistant entities are set so that BOOST mode can be pressed with a button and other modes selectable with a dropdown # - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 30 seconds (with ~2 secs in between to allow it to boot) # -# MQTT Commands +# MQTT Commands # Values will be set in place on the update_interval time, not immediately # Use 00:00 in 24hr format for time setting. (Note there is no weekday/weekend setting) # mqtt_timer_topic/morning-on/06:00 : Time device will go on @@ -56,11 +58,11 @@ substitutions: device_name: "esp-poolpumppower" friendly_name: "Pool Pump Power" description_comment: "Pool Pump Power :: Athom Smart Plug Power V3" - device_area: "Outside" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. - + device_area: "Outside" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + # Project Naming project_name: "Athom Technology.Smart Plug V3" # Project Details - project_version: "v2.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v2.4" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords api_key: !secret esp-poolpumppower_api_key # unfortunately you can't use substitutions inside secrets names @@ -70,17 +72,17 @@ substitutions: # Device Settings log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "20s" # update time for for general sensors etc - + # Device Settings relay_icon: "mdi:pool" - current_limit : "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. + current_limit: "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. mqtt_timer_topic: "viewroad-commands/poolpump-timer" # Topics you will use to change stuff boost_duration_default: "60" # Minutes to stay ON in STARTUP mode before reverting to TIMER morning_on_default: "450" # Default in minutes from midnight. Default 07:30 => 450 morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) evening_on_default: "1260" # Default in minutes from midnight. Default 21:00 => 1260 evening_off_default: "1350" # Default in minutes from midnight. Default 22:30 => 1350 => 1440 is midnight - + ########################################################################################## # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html @@ -106,17 +108,17 @@ packages: 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 + 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 + 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 # Device Specific included packages common_athompowermonV3: !include @@ -132,8 +134,8 @@ esphome: name: "${device_name}" friendly_name: "${friendly_name}" comment: "${description_comment}" #Appears on the esphome page in HA - area: "${device_area}" - name_add_mac_suffix: False + area: "${device_area}" + name_add_mac_suffix: false min_version: 2024.6.0 project: name: "${project_name}" @@ -144,7 +146,7 @@ esphome: board_build.flash_mode: dio on_boot: then: - - script.execute: evaluate_relay_state + - script.execute: evaluate_relay_state ########################################################################################## # ESP Platform and Framework @@ -155,7 +157,7 @@ esp32: flash_size: 4MB variant: ESP32C3 framework: - type: esp-idf # "esp-idf" OR "arduino". Suggested ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. + type: esp-idf # "esp-idf" OR "arduino". Suggested ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. version: recommended # recommended, latest or dev preferences: @@ -164,10 +166,10 @@ preferences: esp32_improv: authorizer: none -########################################################################################## +########################################################################################## # 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) @@ -234,16 +236,6 @@ globals: restore_value: true initial_value: "2" - #################################################### - # current_mins is set if SNTP is invalid. - # We assume user powers on the device at 12:00 noon - # => 12 * 60 = 720 minutes from midnight. - #################################################### - - id: current_mins - type: int - restore_value: true - initial_value: "720" # 720 is 12:00 Noon - #################################################### # boost_timer: counts minutes in BOOST mode # After 'boost_duration' minutes, revert to TIMER. @@ -272,7 +264,7 @@ text_sensor: name: "Morning On Time Setting" id: morning_on_topic topic: "${mqtt_timer_topic}/morning-on" # Stored in the format HH:MM - internal: True + internal: true on_value: then: - lambda: |- @@ -285,6 +277,7 @@ text_sensor: } else { ESP_LOGW("timer","Invalid Morning On format: %s", x.c_str()); } + #################################################### # Morning Off time => "HH:MM" #################################################### @@ -292,7 +285,7 @@ text_sensor: name: "Morning Off Time Setting" id: morning_off_topic topic: "${mqtt_timer_topic}/morning-off" # Stored in the format HH:MM - internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + internal: true # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: - lambda: |- @@ -304,6 +297,7 @@ text_sensor: } else { ESP_LOGW("timer","Invalid Morning Off format: %s", x.c_str()); } + #################################################### # Evening On time => "HH:MM" #################################################### @@ -311,7 +305,7 @@ text_sensor: name: "Evening On Time Setting" id: evening_on_topic topic: "${mqtt_timer_topic}/evening-on" # Stored in the format HH:MM - internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + internal: true # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: - lambda: |- @@ -323,6 +317,7 @@ text_sensor: } else { ESP_LOGW("timer","Invalid Evening On format: %s", x.c_str()); } + #################################################### # Evening Off time => "HH:MM" #################################################### @@ -330,7 +325,7 @@ text_sensor: name: "Evening Off Time Setting" id: evening_off_topic topic: "${mqtt_timer_topic}/evening-off" # Stored in the format HH:MM - internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + internal: true # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: - lambda: |- @@ -342,6 +337,7 @@ text_sensor: } else { ESP_LOGW("timer","Invalid Evening Off format: %s", x.c_str()); } + #################################################### # Boost duration => 1 - 1439 #################################################### @@ -349,7 +345,7 @@ text_sensor: name: "Boost Duration" id: boost_time_topic topic: "${mqtt_timer_topic}/boost-time" # Stored as an integer from 1-1439 - internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + internal: true # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: - lambda: |- @@ -357,7 +353,7 @@ text_sensor: char *endptr; long v = strtol(x.c_str(), &endptr, 10); - // invalid if nothing parsed, trailing chars, or out of 0–1439 + // invalid if nothing parsed, trailing chars, or out of 0-1439 if (endptr == x.c_str() || *endptr != '\0' || v < 0 || v > 1439) { ESP_LOGE("boost_time", "Invalid boost_time '%s'", x.c_str()); } else { @@ -368,13 +364,11 @@ text_sensor: # Subscribe to operation mode: # OFF, ON, TIMER, BOOST # We do case-insensitive compare using strcasecmp - # (Requires typically included in ESPHome) #################################################### - # MQTT subscription: set mode, then immediately re-evaluate relay - platform: mqtt_subscribe id: timer_operation_mode_topic topic: "${mqtt_timer_topic}/operation" - internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + internal: true # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: - lambda: |- @@ -420,7 +414,6 @@ text_sensor: lambda: |- int hour = id(morning_on) / 60; int minute = id(morning_on) % 60; - // Increase to 16 for safety char buff[16]; snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); return { std::string(buff) }; @@ -434,8 +427,6 @@ text_sensor: lambda: |- int hour = id(morning_off) / 60; int minute = id(morning_off) % 60; - // Increase buffer size to 8 just to be safe - // Increase to 16 for safety char buff[16]; snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); return { std::string(buff) }; @@ -449,8 +440,6 @@ text_sensor: lambda: |- int hour = id(evening_on) / 60; int minute = id(evening_on) % 60; - // Increase buffer size to 8 just to be safe - // Increase to 16 for safety char buff[16]; snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); return { std::string(buff) }; @@ -464,8 +453,6 @@ text_sensor: lambda: |- int hour = id(evening_off) / 60; int minute = id(evening_off) % 60; - // Increase buffer size to 8 just to be safe - // Increase to 16 for safety char buff[16]; snprintf(buff, sizeof(buff), "%02d:%02d", hour, minute); return { std::string(buff) }; @@ -491,7 +478,7 @@ binary_sensor: then: - lambda: |- if (id(relay).state) { - // Relay is ON: turn it OFF and set mode to 0 (TIMER) + // Relay is ON: turn it OFF and set mode to 2 (TIMER) id(relay).turn_off(); id(operation_mode) = 2; } else { @@ -499,7 +486,7 @@ binary_sensor: id(relay).turn_on(); id(operation_mode) = 3; } - + - platform: template name: "Relay Status" lambda: |- @@ -518,14 +505,14 @@ sensor: update_interval: "${update_interval}" lambda: |- return id(boost_duration); - + - platform: template name: "Mins from Midnight" id: mins_from_midnight unit_of_measurement: "mins" accuracy_decimals: "0" update_interval: "${update_interval}" - internal: True # No need to show this in Home Assistant + internal: true # No need to show this in Home Assistant lambda: |- return id(current_mins); @@ -541,21 +528,28 @@ sensor: if (!id(relay).state) { return 0; } + int rem = 0; + // only calculate for mode 2 (scheduled) or mode 3 (BOOST) if (id(operation_mode) == 2) { - int a = id(morning_off) - id(current_mins); - int b = id(evening_off) - id(current_mins); - // if a is negative, use b; otherwise pick the smaller of a or b - rem = (a < 0) ? b : (a < b ? a : b); - } - else if (id(operation_mode) == 3) { + // Only report remaining time for the currently-active enabled window + if (id(morning_timer_enable).state && + id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) { + rem = id(morning_off) - id(current_mins); + } else if (id(evening_timer_enable).state && + id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) { + rem = id(evening_off) - id(current_mins); + } else { + rem = 0; + } + } else if (id(operation_mode) == 3) { rem = id(boost_duration) - id(boost_timer); } + // never return negative return rem > 0 ? rem : 0; - ################################################################################################# # SWITCH COMPONENT # https://esphome.io/components/switch/ @@ -567,8 +561,36 @@ switch: id: relay restore_mode: RESTORE_DEFAULT_OFF # Ensures the relay is restored (or off) at boot #internal: true # Hides the switch from Home Assistant - icon: "${relay_icon}" - + icon: "${relay_icon}" + + ################################################################################################# + # TIMER ENABLE SWITCHES (gate the schedule windows) + # These default ON, and restore as ON at reboot (unless changed). + ################################################################################################# + - platform: template + name: "Morning Timer Enable" + id: morning_timer_enable + icon: "mdi:timer-outline" + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + + - platform: template + name: "Evening Timer Enable" + id: evening_timer_enable + icon: "mdi:timer-outline" + optimistic: true + restore_mode: RESTORE_DEFAULT_ON + entity_category: config + on_turn_on: + - script.execute: evaluate_relay_state + on_turn_off: + - script.execute: evaluate_relay_state + ################################################################################################# # BUTTON COMPONENT # https://esphome.io/components/button/index.html @@ -585,6 +607,7 @@ button: id(operation_mode) = 3; # 2) immediately re-evaluate relay state - script.execute: evaluate_relay_state + - platform: template name: "Default timer settings" id: default_timer_settings_button @@ -592,14 +615,15 @@ button: on_press: - lambda: |- // Restore all timing globals to their YAML defaults - id(morning_on) = ${morning_on_default}; - id(morning_off) = ${morning_off_default}; - id(evening_on) = ${evening_on_default}; - id(evening_off) = ${evening_off_default}; - id(boost_duration)= ${boost_duration_default}; + id(morning_on) = ${morning_on_default}; + id(morning_off) = ${morning_off_default}; + id(evening_on) = ${evening_on_default}; + id(evening_off) = ${evening_off_default}; + id(boost_duration) = ${boost_duration_default}; + // Reset mode to TIMER and clear any running boost - id(operation_mode)= 2; - id(boost_timer) = 0; + id(operation_mode) = 2; + id(boost_timer) = 0; ESP_LOGI("timer","Default timer settings applied"); - script.execute: evaluate_relay_state @@ -656,25 +680,36 @@ script: return; } - // OFF → always off + // OFF -> always off if (mode == 0) { id(relay).turn_off(); return; } - // ON → always on + // ON -> always on if (mode == 1) { id(relay).turn_on(); return; } - // TIMER → follow schedule windows + // TIMER -> follow schedule windows (gated by enable switches) { bool should_on = false; - if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) - should_on = true; - if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) - should_on = true; + + // Morning window only applies if Morning Timer Enable is ON + if (id(morning_timer_enable).state) { + if (id(current_mins) >= id(morning_on) && id(current_mins) < id(morning_off)) { + should_on = true; + } + } + + // Evening window only applies if Evening Timer Enable is ON + if (id(evening_timer_enable).state) { + if (id(current_mins) >= id(evening_on) && id(current_mins) < id(evening_off)) { + should_on = true; + } + } + if (should_on) id(relay).turn_on(); else id(relay).turn_off(); } @@ -688,7 +723,7 @@ interval: - interval: "1min" then: - lambda: |- - // — update current_mins via SNTP or fallback + // - update current_mins via SNTP or fallback if (!id(sntp_time).now().is_valid()) { id(current_mins)++; if (id(current_mins) >= 1440) id(current_mins) = 0; @@ -697,7 +732,7 @@ interval: id(current_mins) = now.hour * 60 + now.minute; } - // — if in BOOST, advance boost_timer and expire when done + // - if in BOOST, advance boost_timer and expire when done if (id(operation_mode) == 3) { id(boost_timer)++; if (id(boost_timer) >= id(boost_duration)) { @@ -706,4 +741,3 @@ interval: } } - script.execute: evaluate_relay_state - diff --git a/packages/robotdyn_dimmers.yaml b/packages/robotdyn_dimmers.yaml index 41ff2c7..3f2b088 100644 --- a/packages/robotdyn_dimmers.yaml +++ b/packages/robotdyn_dimmers.yaml @@ -1,12 +1,19 @@ mqtt: - light: schema: json - name: "Lounge Dimmer" + name: "Robodyn Dimmer" unique_id: lounge_dimmer_1 + default_entity_id: light.robodyn_dimmer + state_topic: "livingroom/dimmer" command_topic: "livingroom/dimmer/set" + availability: - - topic: "livingroom/availability" + - topic: "livingroom/availablity" + payload_available: "online" + payload_not_available: "offline" + brightness: true - supported_color_modes: ["brightness"] + supported_color_modes: + - brightness retain: true diff --git a/packages/shower_controls.yaml b/packages/shower_controls.yaml index add2b94..3c121cb 100644 --- a/packages/shower_controls.yaml +++ b/packages/shower_controls.yaml @@ -1,7 +1,43 @@ +input_boolean: + main_bathroom_shower: + name: Main Bathroom Shower + icon: mdi:shower + sensor: - platform: derivative name: Main Bathroom Humidity Change Rate source: sensor.main_bathroom_environment_zth10_humidity_2 - round: 1 - unit_time: s # the resulting "unit_of_measurement" will be %H/s if the sensor.temperate has set %H as its unit - time_window: "00:00:10" # we look at the change over the last 10 seconds + unit_time: min + time_window: "00:15:00" + round: 0.1 + +automation: + - id: main_bathroom_shower_detected + alias: Main Bathroom - Shower detected (humidity rate + min humidity) + mode: single + trigger: + - platform: numeric_state + entity_id: sensor.main_bathroom_humidity_change_rate + above: 0.6 + for: "00:05:00" + condition: + - condition: numeric_state + entity_id: sensor.main_bathroom_environment_zth10_humidity_2 + above: 63 + action: + - service: input_boolean.turn_on + target: + entity_id: input_boolean.main_bathroom_shower + + - id: main_bathroom_shower_cleared + alias: Main Bathroom - Shower cleared (humidity rate) + mode: single + trigger: + - platform: numeric_state + entity_id: sensor.main_bathroom_humidity_change_rate + below: 0.2 + for: "00:15:00" + action: + - service: input_boolean.turn_off + target: + entity_id: input_boolean.main_bathroom_shower